@h-ai/serv 0.1.0-alpha.15
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/LICENSE +202 -0
- package/README.md +515 -0
- package/dist/chunk-27PCPE37.js +17 -0
- package/dist/chunk-27PCPE37.js.map +1 -0
- package/dist/chunk-4QBF4PUG.js +128 -0
- package/dist/chunk-4QBF4PUG.js.map +1 -0
- package/dist/chunk-YQV2OMQK.js +16 -0
- package/dist/chunk-YQV2OMQK.js.map +1 -0
- package/dist/features/ai.d.ts +361 -0
- package/dist/features/ai.js +206 -0
- package/dist/features/ai.js.map +1 -0
- package/dist/features/iam.d.ts +774 -0
- package/dist/features/iam.js +145 -0
- package/dist/features/iam.js.map +1 -0
- package/dist/features/storage.d.ts +163 -0
- package/dist/features/storage.js +94 -0
- package/dist/features/storage.js.map +1 -0
- package/dist/index.d.ts +646 -0
- package/dist/index.js +809 -0
- package/dist/index.js.map +1 -0
- package/dist/serv-context-CwewduFi.d.ts +155 -0
- package/package.json +82 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
import * as _h_ai_core from '@h-ai/core';
|
|
2
|
+
import { HaiResult, ValidationFormError } from '@h-ai/core';
|
|
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';
|
|
7
|
+
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
|
+
export { d as CreateServContextInput, e as RefreshTokenPair } from './serv-context-CwewduFi.js';
|
|
9
|
+
import { AnyContractRouter, OpenAPI } from '@orpc/contract';
|
|
10
|
+
import { z, ZodType } from 'zod';
|
|
11
|
+
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;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @h-ai/serv — Pipeline 共享类型
|
|
125
|
+
*
|
|
126
|
+
* 统一定义 Hono middleware 与 oRPC procedure wrapper 的最小公共类型,
|
|
127
|
+
* 供默认实现与使用方自定义实现复用。
|
|
128
|
+
* @module pipelines/serv-pipeline-types
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
/** Serv 层 Hono middleware。 */
|
|
132
|
+
type ServMiddleware = MiddlewareHandler;
|
|
133
|
+
/**
|
|
134
|
+
* Hono middleware 工厂类型。
|
|
135
|
+
*
|
|
136
|
+
* - 无配置 middleware:`ServMiddlewareFactory<void>`
|
|
137
|
+
* - 带配置 middleware:`ServMiddlewareFactory<MyConfig>`
|
|
138
|
+
*/
|
|
139
|
+
type ServMiddlewareFactory<TConfig = void> = [TConfig] extends [void] ? () => ServMiddleware : (config: TConfig) => ServMiddleware;
|
|
140
|
+
/** oRPC procedure handler 的最小上下文约束。 */
|
|
141
|
+
interface ServProcedureOptions<TInput = unknown> {
|
|
142
|
+
readonly input: TInput;
|
|
143
|
+
readonly context: ServContext;
|
|
144
|
+
}
|
|
145
|
+
/** oRPC procedure handler。 */
|
|
146
|
+
type ServProcedureHandler<TInput, TOutput> = (options: ServProcedureOptions<TInput>) => HaiResult<TOutput> | Promise<HaiResult<TOutput>>;
|
|
147
|
+
/** 单参数 procedure 包装器(例如 `mapHaiError`、`requireAuth`)。 */
|
|
148
|
+
type ServProcedureWrapper = <TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>) => ServProcedureHandler<TInput, TOutput>;
|
|
149
|
+
/** 带额外参数的 procedure 包装器(例如 `requirePermission('x', handler)`)。 */
|
|
150
|
+
type ServGuardedProcedureWrapper<TRequirement> = <TInput, TOutput>(requirement: TRequirement, handler: ServProcedureHandler<TInput, TOutput>) => ServProcedureHandler<TInput, TOutput>;
|
|
151
|
+
|
|
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
|
+
/**
|
|
246
|
+
* @h-ai/serv — HTTP 配置
|
|
247
|
+
*
|
|
248
|
+
* 提供用于控制 API 前缀、OpenAPI、docs、健康检查、内部 RPC 与传输加密策略的配置结构,
|
|
249
|
+
* 并由 zod 负责默认值填充与边界校验。
|
|
250
|
+
* @module serv-config
|
|
251
|
+
*/
|
|
252
|
+
|
|
253
|
+
/** OpenAPI JSON endpoint 配置。 */
|
|
254
|
+
interface ServOpenAPIHttpConfig {
|
|
255
|
+
readonly path: `/${string}`;
|
|
256
|
+
}
|
|
257
|
+
/** 文档页面 endpoint 配置。 */
|
|
258
|
+
interface ServDocsHttpConfig {
|
|
259
|
+
readonly path: `/${string}`;
|
|
260
|
+
readonly requireAuth?: boolean;
|
|
261
|
+
}
|
|
262
|
+
/** 健康检查 endpoint 配置。 */
|
|
263
|
+
interface ServHealthHttpConfig {
|
|
264
|
+
readonly path: `/${string}`;
|
|
265
|
+
readonly readyPath?: `/${string}`;
|
|
266
|
+
}
|
|
267
|
+
/** 内部 RPC endpoint 配置。 */
|
|
268
|
+
interface ServRpcHttpConfig {
|
|
269
|
+
readonly prefix: `/${string}`;
|
|
270
|
+
readonly access: 'loopback' | 'private-network' | 'gateway-only';
|
|
271
|
+
readonly gatewayHeader?: string;
|
|
272
|
+
readonly gatewaySecret?: string;
|
|
273
|
+
}
|
|
274
|
+
/** 传输加密运行时配置(对应 `config/_serv.yml`)。 */
|
|
275
|
+
interface ServTransportRuntimeConfig {
|
|
276
|
+
readonly keyExchangePath: `/${string}`;
|
|
277
|
+
readonly excludePaths: readonly `/${string}`[];
|
|
278
|
+
readonly maxClients: number;
|
|
279
|
+
}
|
|
280
|
+
/** Hono app 的 HTTP 挂载配置。 */
|
|
281
|
+
interface ServHttpConfig {
|
|
282
|
+
readonly apiPrefix: `/api/${string}`;
|
|
283
|
+
readonly openapi: false | ServOpenAPIHttpConfig;
|
|
284
|
+
readonly docs: false | ServDocsHttpConfig;
|
|
285
|
+
readonly health: false | ServHealthHttpConfig;
|
|
286
|
+
readonly rpc: false | ServRpcHttpConfig;
|
|
287
|
+
}
|
|
288
|
+
/** `config/_serv.yml` 的顶层配置结构。 */
|
|
289
|
+
interface ServConfig {
|
|
290
|
+
readonly http: ServHttpConfig;
|
|
291
|
+
readonly transport: false | ServTransportRuntimeConfig;
|
|
292
|
+
}
|
|
293
|
+
/** `config/_serv.yml` 中的传输加密输入类型。 */
|
|
294
|
+
type ServTransportRuntimeConfigInput = Partial<{
|
|
295
|
+
readonly keyExchangePath: `/${string}`;
|
|
296
|
+
readonly excludePaths: readonly `/${string}`[];
|
|
297
|
+
readonly maxClients: number;
|
|
298
|
+
}>;
|
|
299
|
+
/** `serv.createApp` 接受的 HTTP 配置入参类型。 */
|
|
300
|
+
type ServHttpConfigInput = Partial<{
|
|
301
|
+
readonly apiPrefix: `/api/${string}`;
|
|
302
|
+
readonly openapi: false | Partial<ServOpenAPIHttpConfig>;
|
|
303
|
+
readonly docs: false | Partial<ServDocsHttpConfig>;
|
|
304
|
+
readonly health: false | Partial<ServHealthHttpConfig>;
|
|
305
|
+
readonly rpc: false | Partial<ServRpcHttpConfig>;
|
|
306
|
+
}>;
|
|
307
|
+
/** `config/_serv.yml` 的顶层输入类型。 */
|
|
308
|
+
interface ServConfigInput {
|
|
309
|
+
readonly http?: ServHttpConfigInput;
|
|
310
|
+
readonly transport?: false | ServTransportRuntimeConfigInput;
|
|
311
|
+
}
|
|
312
|
+
/** `config/_serv.yml` 对应的配置 Schema。 */
|
|
313
|
+
declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
314
|
+
http: z.ZodOptional<z.ZodObject<{
|
|
315
|
+
apiPrefix: z.ZodDefault<z.ZodCustom<`/api/${string}`, `/api/${string}`>>;
|
|
316
|
+
openapi: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
317
|
+
path: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
|
|
318
|
+
}, z.core.$strip>]>>;
|
|
319
|
+
docs: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
320
|
+
path: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
|
|
321
|
+
requireAuth: z.ZodOptional<z.ZodBoolean>;
|
|
322
|
+
}, z.core.$strip>]>>;
|
|
323
|
+
health: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
324
|
+
path: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
|
|
325
|
+
readyPath: z.ZodDefault<z.ZodOptional<z.ZodCustom<`/${string}`, `/${string}`>>>;
|
|
326
|
+
}, z.core.$strip>]>>;
|
|
327
|
+
rpc: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
328
|
+
prefix: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
|
|
329
|
+
access: z.ZodDefault<z.ZodEnum<{
|
|
330
|
+
loopback: "loopback";
|
|
331
|
+
"private-network": "private-network";
|
|
332
|
+
"gateway-only": "gateway-only";
|
|
333
|
+
}>>;
|
|
334
|
+
gatewayHeader: z.ZodOptional<z.ZodString>;
|
|
335
|
+
gatewaySecret: z.ZodOptional<z.ZodString>;
|
|
336
|
+
}, z.core.$strip>]>>;
|
|
337
|
+
}, z.core.$strip>>;
|
|
338
|
+
transport: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
339
|
+
keyExchangePath: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
|
|
340
|
+
excludePaths: z.ZodDefault<z.ZodArray<z.ZodCustom<`/${string}`, `/${string}`>>>;
|
|
341
|
+
maxClients: z.ZodDefault<z.ZodNumber>;
|
|
342
|
+
}, z.core.$strip>]>>;
|
|
343
|
+
}, z.core.$strip>, z.ZodTransform<{
|
|
344
|
+
http: ServHttpConfig;
|
|
345
|
+
transport: false | ServTransportRuntimeConfig;
|
|
346
|
+
}, {
|
|
347
|
+
http?: {
|
|
348
|
+
apiPrefix: `/api/${string}`;
|
|
349
|
+
openapi: false | {
|
|
350
|
+
path: `/${string}`;
|
|
351
|
+
};
|
|
352
|
+
docs: false | {
|
|
353
|
+
path: `/${string}`;
|
|
354
|
+
requireAuth?: boolean | undefined;
|
|
355
|
+
};
|
|
356
|
+
health: false | {
|
|
357
|
+
path: `/${string}`;
|
|
358
|
+
readyPath: `/${string}`;
|
|
359
|
+
};
|
|
360
|
+
rpc: false | {
|
|
361
|
+
prefix: `/${string}`;
|
|
362
|
+
access: "loopback" | "private-network" | "gateway-only";
|
|
363
|
+
gatewayHeader?: string | undefined;
|
|
364
|
+
gatewaySecret?: string | undefined;
|
|
365
|
+
};
|
|
366
|
+
} | undefined;
|
|
367
|
+
transport?: false | {
|
|
368
|
+
keyExchangePath: `/${string}`;
|
|
369
|
+
excludePaths: `/${string}`[];
|
|
370
|
+
maxClients: number;
|
|
371
|
+
} | undefined;
|
|
372
|
+
}>>;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @h-ai/serv — 传输加密集成
|
|
376
|
+
*
|
|
377
|
+
* 提供 Hono 中间件 + 密钥协商端点,直接依赖 `@h-ai/crypto` 的
|
|
378
|
+
* `TransportEncryptionManager` 与 `TRANSPORT_PROTOCOL` 协议常量,
|
|
379
|
+
* 确保前后端协议不漂移。
|
|
380
|
+
*
|
|
381
|
+
* 单次请求的处理顺序:
|
|
382
|
+
* 1. 特判密钥协商端点
|
|
383
|
+
* 2. 跳过不参与加解密的路径
|
|
384
|
+
* 3. 校验 clientId 与已注册公钥
|
|
385
|
+
* 4. 解密请求体并把明文 request 回写给下游
|
|
386
|
+
* 5. 执行业务逻辑
|
|
387
|
+
* 6. 尝试加密 JSON 响应
|
|
388
|
+
*
|
|
389
|
+
* @module serv-transport
|
|
390
|
+
*/
|
|
391
|
+
|
|
392
|
+
/** {@link createApp} 的 `transport` 配置项。 */
|
|
393
|
+
interface ServTransportConfig {
|
|
394
|
+
/**
|
|
395
|
+
* crypto 服务实例。serv 通过 `crypto.transport.createServer()` 创建
|
|
396
|
+
* 传输加密管理器,并使用 `crypto.transport.protocol` 中的协议常量。
|
|
397
|
+
*/
|
|
398
|
+
readonly crypto: CryptoFunctions;
|
|
399
|
+
/**
|
|
400
|
+
* 密钥协商端点路径(相对于 `apiPrefix`)。默认 `/_hai/key-exchange`。
|
|
401
|
+
*/
|
|
402
|
+
readonly keyExchangePath?: string;
|
|
403
|
+
/**
|
|
404
|
+
* 不参与加解密的路径白名单(绝对路径完整匹配或前缀匹配)。
|
|
405
|
+
* 健康检查、文档页等公共路由通常无需加密。
|
|
406
|
+
*/
|
|
407
|
+
readonly excludePaths?: readonly string[];
|
|
408
|
+
/** 服务端可缓存的客户端公钥数量上限。默认 10000。 */
|
|
409
|
+
readonly maxClients?: number;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* @h-ai/serv — Hono app 装配
|
|
414
|
+
*
|
|
415
|
+
* 将 oRPC contract + procedures + http 配置组合为可运行的 Hono app,
|
|
416
|
+
* 自动挂载安全响应头、健康检查、OpenAPI JSON、Scalar 文档页与 oRPC handler。
|
|
417
|
+
*
|
|
418
|
+
* 关键装配顺序:
|
|
419
|
+
* 1. 解析 HTTP 配置
|
|
420
|
+
* 2. 选择请求上下文工厂
|
|
421
|
+
* 3. 挂基础安全 middleware
|
|
422
|
+
* 4. (可选)挂传输加密 middleware
|
|
423
|
+
* 5. 挂自定义 middleware(若提供)
|
|
424
|
+
* 6. 挂基础路由(health / refresh-cookie)
|
|
425
|
+
* 7. 挂业务路由(oRPC / RPC)
|
|
426
|
+
* 8. 挂文档路由(openapi / docs)
|
|
427
|
+
* @module serv-app
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* `serv.createApp()` 的自定义 middleware 挂载项。
|
|
432
|
+
*
|
|
433
|
+
* - `path` 省略时默认挂到 `'*'`
|
|
434
|
+
* - 按数组顺序注册
|
|
435
|
+
* - 注册位置固定在内置 `securityHeaders` / `transport` 之后、业务路由之前
|
|
436
|
+
*/
|
|
437
|
+
interface ServMiddlewareMount {
|
|
438
|
+
readonly path?: string;
|
|
439
|
+
readonly middleware: ServMiddleware;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* 创建 Hono app 的配置。
|
|
443
|
+
*
|
|
444
|
+
* **认证 / 会话填充设计**(遵循最小知识原则):
|
|
445
|
+
*
|
|
446
|
+
* - **`iam`**(推荐):单一顶层引用。提供后 serv 自动:
|
|
447
|
+
* 1. 使用 `iam.session.verifyToken` 填充 `context.session` → `requireAuth/Permission/Role` 生效
|
|
448
|
+
* 2. 若同时启用 `refreshCookie`,使用 `iam.session.refresh` 实现 cookie 刷新
|
|
449
|
+
*
|
|
450
|
+
* - **`refreshCookie`**(可选):opt-in httpOnly cookie 传输 refresh token。与 `iam` 正交:
|
|
451
|
+
* `iam` 决定 *谁* 验证 token,`refreshCookie` 决定 refresh token *怎么* 传输。
|
|
452
|
+
*
|
|
453
|
+
* - **`verifyToken`**(高级):不使用 IAM 模块的逆脱口。优先于 `iam.session.verifyToken`。
|
|
454
|
+
*
|
|
455
|
+
* - **`createContext`**(高级):完全接管上下文构造,设置后 serv **不再**自动填充 session,
|
|
456
|
+
* 由调用方负责。
|
|
457
|
+
*/
|
|
458
|
+
interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>> {
|
|
459
|
+
readonly contract: TContract;
|
|
460
|
+
readonly procedures: TProcedures;
|
|
461
|
+
readonly http?: ServHttpConfigInput;
|
|
462
|
+
/**
|
|
463
|
+
* 自定义 Hono middleware。
|
|
464
|
+
*
|
|
465
|
+
* 典型用途:请求日志、trace、指标、CORS、限流、租户头校验等 HTTP 层横切逻辑。
|
|
466
|
+
* 这些 middleware 会在内置安全头 / 传输加密之后、health/oRPC/OpenAPI/docs 路由之前注册。
|
|
467
|
+
*/
|
|
468
|
+
readonly middlewares?: readonly ServMiddlewareMount[];
|
|
469
|
+
/**
|
|
470
|
+
* IAM 模块引用(推荐)。提供后 serv 自动使用 `iam.session.verifyToken` 填充 session,
|
|
471
|
+
* 以及在 `refreshCookie` 启用时使用 `iam.session.refresh` 实现 cookie 刷新。
|
|
472
|
+
*
|
|
473
|
+
* 类型上要求 {@link ServIam}(`iam` 可以直接传入,结构类型保证兼容)。
|
|
474
|
+
*/
|
|
475
|
+
readonly iam?: ServIam;
|
|
476
|
+
/**
|
|
477
|
+
* 启用 httpOnly Cookie 传输 refresh token(opt-in)。
|
|
478
|
+
* 需配合 `iam` 或 `refreshCookie.onRefresh` 使用。
|
|
479
|
+
*/
|
|
480
|
+
readonly refreshCookie?: RefreshCookieConfig;
|
|
481
|
+
/**
|
|
482
|
+
* 高级:自定义访问令牌校验回调,覆盖 `iam.session.verifyToken`。
|
|
483
|
+
* 不使用 IAM 模块的场景下使用。
|
|
484
|
+
*/
|
|
485
|
+
readonly verifyToken?: (token: string) => Promise<HaiResult<ServSession>>;
|
|
486
|
+
/**
|
|
487
|
+
* 高级:完全接管上下文工厂。设置后上述 `iam` / `verifyToken` 均不生效(session 不会被自动填充)。
|
|
488
|
+
*/
|
|
489
|
+
readonly createContext?: CreateServContext;
|
|
490
|
+
/**
|
|
491
|
+
* 启用传输加密(opt-in)。提供后 serv 自动:
|
|
492
|
+
* 1. 调用 `crypto.transport.createServer()` 创建管理器
|
|
493
|
+
* 2. 在 oRPC 路由之前挂载中间件,自动解密请求 / 加密响应
|
|
494
|
+
* 3. 在 `${apiPrefix}${keyExchangePath}` 暴露 POST 密钥协商端点
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```ts
|
|
498
|
+
* serv.createApp({ contract, procedures, transport: { crypto } })
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
readonly transport?: ServTransportConfig;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* 创建并装配 Hono API app。
|
|
505
|
+
*
|
|
506
|
+
* @param options - contract/procedures/http 三段式配置
|
|
507
|
+
* @returns Hono app
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```ts
|
|
511
|
+
* const app = serv.createApp({
|
|
512
|
+
* contract,
|
|
513
|
+
* procedures,
|
|
514
|
+
* http: {
|
|
515
|
+
* apiPrefix: '/api/v1',
|
|
516
|
+
* openapi: { path: '/openapi.json' },
|
|
517
|
+
* docs: { path: '/docs' },
|
|
518
|
+
* },
|
|
519
|
+
* })
|
|
520
|
+
* serv.listen(app, { port: 3000, host: '0.0.0.0' })
|
|
521
|
+
* ```
|
|
522
|
+
*/
|
|
523
|
+
declare function createApp<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>>(options: CreateServAppOptions<TContract, TProcedures>): Hono;
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* @h-ai/serv — OpenAPI 文档
|
|
527
|
+
*
|
|
528
|
+
* 由 oRPC contract 生成 OpenAPI 3.1 spec,并集中处理 Scalar 文档页 HTML
|
|
529
|
+
* 与本地脚本资源读取逻辑。
|
|
530
|
+
* @module serv-openapi
|
|
531
|
+
*/
|
|
532
|
+
|
|
533
|
+
/** OpenAPI spec 生成配置。 */
|
|
534
|
+
interface GenerateOpenAPISpecOptions {
|
|
535
|
+
readonly title?: string;
|
|
536
|
+
readonly version?: string;
|
|
537
|
+
readonly apiPrefix?: string;
|
|
538
|
+
readonly description?: string;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* 由应用级 contract 生成 OpenAPI 3.1 spec。
|
|
542
|
+
*
|
|
543
|
+
* @param contract - 已组合完成的应用级 oRPC contract
|
|
544
|
+
* @param options - 文档元信息
|
|
545
|
+
* @returns OpenAPI 文档对象
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* const spec = await serv.generateSpec(contract, {
|
|
550
|
+
* title: 'My API',
|
|
551
|
+
* version: '1.0.0',
|
|
552
|
+
* apiPrefix: '/api/v1',
|
|
553
|
+
* })
|
|
554
|
+
* ```
|
|
555
|
+
*/
|
|
556
|
+
declare function generateSpec(contract: AnyContractRouter, options?: GenerateOpenAPISpecOptions): Promise<OpenAPI.Document>;
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* @h-ai/serv — 校验工具
|
|
560
|
+
*
|
|
561
|
+
* 为 API service 提供与 kit 对等的校验 i18n 能力:
|
|
562
|
+
* - {@link resolveRequestLocale}:从请求头(`x-hai-locale` / `Accept-Language`)解析语言。
|
|
563
|
+
* - {@link validateInputOrFail}:在 procedure 内显式校验数据,失败返回 `HaiResult` 错误。
|
|
564
|
+
* - oRPC contract 输入校验失败的自动本地化由 `serv-app.ts` 中的 oRPC 响应拦截器处理。
|
|
565
|
+
*
|
|
566
|
+
* @module serv-validation
|
|
567
|
+
*/
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* 校验失败响应体(含 i18n 错误列表)。
|
|
571
|
+
*
|
|
572
|
+
* 复用 `HaiResult` 失败分支结构。字段错误同时在两处出现:
|
|
573
|
+
* - `error.cause`:与 `validateInputOrFail` 输出对齐,HaiResult 标准位置
|
|
574
|
+
* - 顶层 `errors`:保留以兼容已有客户端
|
|
575
|
+
*/
|
|
576
|
+
interface ServValidationFailureBody {
|
|
577
|
+
readonly success: false;
|
|
578
|
+
readonly error: {
|
|
579
|
+
readonly code: string | number;
|
|
580
|
+
readonly message: string;
|
|
581
|
+
readonly httpStatus: number;
|
|
582
|
+
readonly system: string;
|
|
583
|
+
readonly module: string;
|
|
584
|
+
readonly cause?: readonly ValidationFormError[];
|
|
585
|
+
};
|
|
586
|
+
readonly errors: readonly ValidationFormError[];
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* 从请求头解析客户端期望 locale。
|
|
590
|
+
*
|
|
591
|
+
* 优先级:`x-hai-locale` > `Accept-Language` 第一个匹配项 > 默认值(`zh-CN`)。
|
|
592
|
+
* 返回值会被规范化为 serv 当前支持的 locale(如 `en` → `en-US`)。
|
|
593
|
+
*
|
|
594
|
+
* @param headers - 请求头(`Headers` 或与之等价的对象)
|
|
595
|
+
* @returns 解析得到的 locale
|
|
596
|
+
*/
|
|
597
|
+
declare function resolveRequestLocale(headers: Headers | Record<string, string | undefined>): string;
|
|
598
|
+
/**
|
|
599
|
+
* 在 procedure handler 内显式校验数据。
|
|
600
|
+
*
|
|
601
|
+
* 与 oRPC contract 自动校验不同,本函数用于业务层二次校验(例如对从数据库读到的对象再校验)。
|
|
602
|
+
* 失败时返回 `HaiResult` 错误分支,错误的 `cause` 字段携带 `ValidationFormError[]`。
|
|
603
|
+
*
|
|
604
|
+
* 推荐使用方式:
|
|
605
|
+
* ```ts
|
|
606
|
+
* import { z } from 'zod'
|
|
607
|
+
* import { serv } from '@h-ai/serv'
|
|
608
|
+
*
|
|
609
|
+
* const Schema = z.object({ name: z.string().min(1) })
|
|
610
|
+
*
|
|
611
|
+
* export const handler = serv.mapHaiError(async ({ input, context }) => {
|
|
612
|
+
* const result = serv.validateInputOrFail(Schema, input, context.locale)
|
|
613
|
+
* if (!result.success) return result
|
|
614
|
+
* const { name } = result.data
|
|
615
|
+
* // ...
|
|
616
|
+
* })
|
|
617
|
+
* ```
|
|
618
|
+
*
|
|
619
|
+
* @param schema - Zod schema
|
|
620
|
+
* @param input - 待校验数据
|
|
621
|
+
* @param locale - 期望 locale(通常来自请求上下文)
|
|
622
|
+
*/
|
|
623
|
+
declare function validateInputOrFail<TOutput>(schema: ZodType<TOutput>, input: unknown, locale?: string): HaiResult<TOutput>;
|
|
624
|
+
|
|
625
|
+
/** hai-framework API service runtime 统一入口(扁平 API)。 */
|
|
626
|
+
declare const serv: {
|
|
627
|
+
createApp: typeof createApp;
|
|
628
|
+
parseRequestContext: typeof parseRequestContext;
|
|
629
|
+
buildAuthContextFactory: typeof buildAuthContextFactory;
|
|
630
|
+
resolveRequestLocale: typeof resolveRequestLocale;
|
|
631
|
+
listen: typeof listen;
|
|
632
|
+
toFetch: typeof toFetch;
|
|
633
|
+
generateSpec: typeof generateSpec;
|
|
634
|
+
/** 基于 contract 创建可注入 context 的 procedure builder(封装 `@orpc/server` 的 `implement`)。 */
|
|
635
|
+
implement: typeof implement;
|
|
636
|
+
requireAuth: typeof requireAuth;
|
|
637
|
+
requirePermission: typeof requirePermission;
|
|
638
|
+
requireRole: typeof requireRole;
|
|
639
|
+
mapHaiError: typeof mapHaiError;
|
|
640
|
+
validateInputOrFail: typeof validateInputOrFail;
|
|
641
|
+
WILDCARD_PERMISSION: string;
|
|
642
|
+
WILDCARD_ROLE: string;
|
|
643
|
+
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
|
+
};
|
|
645
|
+
|
|
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 };
|