@h-ai/serv 0.1.0-alpha.67 → 0.1.0-alpha.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,14 +2,16 @@ import * as _h_ai_core from '@h-ai/core';
2
2
  import { HaiResult, ValidationFormError } from '@h-ai/core';
3
3
  import { AIFunctions, AudioWsStartMessage, AudioFormat } from '@h-ai/ai';
4
4
  import { AnyContractRouter, OpenAPI } from '@orpc/contract';
5
- import { ProcedureHandler, ErrorMap, Meta, ProcedureHandlerOptions, ORPCErrorConstructorMap, Router, ContractRouter, Context, ContractProcedure, InferSchemaInput, InferSchemaOutput, UnlaziedRouter } from '@orpc/server';
6
- import { a as ServSession, S as ServContext, b as ServIam, R as RefreshCookieConfig, C as CreateServContext, c as ServRequestMetadata, p as parseRequestContext, d as buildAuthContextFactory } from './serv-context-CWzyrsdq.js';
7
- export { e as CreateServContextInput, N as NativeRefreshTokenTransportConfig, f as RefreshTokenPair } from './serv-context-CWzyrsdq.js';
5
+ import { ProcedureHandler, ErrorMap, Meta, ProcedureHandlerOptions, ORPCErrorConstructorMap, Router } from '@orpc/server';
6
+ import { a as ServSession, S as ServContext, b as ServIam, R as RefreshCookieConfig, C as CreateServContext, c as ServRequestMetadata, p as parseRequestContext, d as buildAuthContextFactory } from './serv-context-C48WdBcd.js';
7
+ export { e as CreateServContextInput, N as NativeRefreshTokenTransportConfig, f as RefreshTokenPair } from './serv-context-C48WdBcd.js';
8
8
  import { MiddlewareHandler } from 'hono';
9
9
  import { z, ZodType } from 'zod';
10
- import { CryptoFunctions, TransportKeyStore } from '@h-ai/crypto';
10
+ import { TransportServerKeyRing, CryptoFunctions, TransportKeyStore } from '@h-ai/crypto';
11
11
  import { ServerType } from '@hono/node-server';
12
12
  import { AddressInfo } from 'node:net';
13
+ import { i as implement } from './serv-router-BX8hgYxo.js';
14
+ export { A as AuthenticatedServContext, S as ServImplementer, a as ServRouteHandler, b as ServRoutePath, c as ServRouterBuilder } from './serv-router-BX8hgYxo.js';
13
15
  import { StorageFunctions } from '@h-ai/storage';
14
16
 
15
17
  /**
@@ -26,7 +28,18 @@ import { StorageFunctions } from '@h-ai/storage';
26
28
  * @module features/serv-feature-audio
27
29
  */
28
30
 
29
- /** 语音 WebSocket 接入依赖。 */
31
+ /**
32
+ * 语音 WebSocket 接入依赖。
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * // 模块内部上下文:示例复用当前声明所在模块的实际绑定。
37
+ * function withAudioLimits(deps: AudioWsDeps): AudioWsDeps {
38
+ * return { ...deps, maxSessionMs: 60000, maxBufferedBytes: 1024 * 1024 }
39
+ * }
40
+ * // verifyTicket 必须原子消费共享的一次性票据,不能仅解析客户端身份。
41
+ * ```
42
+ */
30
43
  interface AudioWsDeps {
31
44
  /** AI 服务对象的语音能力(`ai.audio`)。 */
32
45
  readonly ai: Pick<AIFunctions, 'audio'>;
@@ -59,21 +72,67 @@ interface AudioWsDeps {
59
72
  * @module pipelines/serv-pipeline-types
60
73
  */
61
74
 
62
- /** Serv 层 Hono middleware。 */
75
+ /**
76
+ * Serv 层 Hono middleware。
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import type { ServMiddleware } from '@h-ai/serv'
81
+ * import { serv } from '@h-ai/serv'
82
+ *
83
+ * const middleware: ServMiddleware = async (context, next) => {
84
+ * context.header('X-App-Version', '1')
85
+ * await next()
86
+ * }
87
+ * ```
88
+ */
63
89
  type ServMiddleware = MiddlewareHandler;
64
90
  /**
65
91
  * Hono middleware 工厂类型。
66
92
  *
67
93
  * - 无配置 middleware:`ServMiddlewareFactory<void>`
68
94
  * - 带配置 middleware:`ServMiddlewareFactory<MyConfig>`
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * import type { ServMiddlewareFactory } from '@h-ai/serv'
99
+ * import { serv } from '@h-ai/serv'
100
+ *
101
+ * const createTrace: ServMiddlewareFactory<{ header: string }> = config => async (context, next) => {
102
+ * context.header(config.header, 'enabled')
103
+ * await next()
104
+ * }
105
+ * const middleware = createTrace({ header: 'X-Trace-Enabled' })
106
+ * ```
69
107
  */
70
108
  type ServMiddlewareFactory<TConfig = void> = [TConfig] extends [void] ? () => ServMiddleware : (config: TConfig) => ServMiddleware;
71
109
  /**
72
110
  * 使用 oRPC 公开 `ProcedureHandler` 定义的 HaiResult procedure handler。
73
111
  *
74
112
  * handler options 完整保留 oRPC 的路径、procedure、signal 与错误构造器信息。
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * import type { ServProcedureOptions } from '@h-ai/serv'
117
+ * import { serv } from '@h-ai/serv'
118
+ *
119
+ * function requestId(options: ServProcedureOptions<{ id: string }>) {
120
+ * return { id: options.input.id, requestId: options.context.requestId }
121
+ * }
122
+ * ```
75
123
  */
76
124
  type ServProcedureOptions<TInput = unknown, TContext extends ServContext = ServContext> = ProcedureHandlerOptions<TContext, TInput, ORPCErrorConstructorMap<ErrorMap>, Meta>;
125
+ /**
126
+ * 基于 oRPC 真实 options 的 HaiResult handler;领域失败返回错误分支,异常由 route 包装收口。
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * import type { ServProcedureHandler } from '@h-ai/serv'
131
+ * import { serv } from '@h-ai/serv'
132
+ *
133
+ * const handler: ServProcedureHandler<{ id: string }, { id: string }> = async ({ input }) => ({ success: true, data: { id: input.id } })
134
+ * ```
135
+ */
77
136
  type ServProcedureHandler<TInput, TOutput, TContext extends ServContext = ServContext> = ProcedureHandler<TContext, TInput, HaiResult<TOutput>, ErrorMap, Meta>;
78
137
 
79
138
  /**
@@ -84,41 +143,125 @@ type ServProcedureHandler<TInput, TOutput, TContext extends ServContext = ServCo
84
143
  * @module serv-config
85
144
  */
86
145
 
87
- /** 服务器监听配置(对应 `config/_serv.yml` 的 `server`)。 */
146
+ /**
147
+ * 服务器监听配置(对应 `config/_serv.yml` 的 `server`)。
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * import type { ServServerConfig } from '@h-ai/serv'
152
+ * import { serv } from '@h-ai/serv'
153
+ *
154
+ * const config: ServServerConfig = { host: '127.0.0.1', port: 3000 }
155
+ * const bindAddress = config.host
156
+ * ```
157
+ */
88
158
  interface ServServerConfig {
89
159
  readonly host: string;
90
160
  readonly port: number;
91
161
  }
92
- /** `config/_serv.yml` 中的服务器监听输入类型。 */
162
+ /**
163
+ * `config/_serv.yml` 中的服务器监听输入类型。
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * import type { ServServerConfigInput } from '@h-ai/serv'
168
+ * import { serv } from '@h-ai/serv'
169
+ *
170
+ * const input: ServServerConfigInput = { port: 3000 }
171
+ * // HOST/PORT 的环境覆盖由配置解析完成后传给 listen。
172
+ * ```
173
+ */
93
174
  type ServServerConfigInput = Partial<ServServerConfig>;
94
- /** OpenAPI JSON endpoint 配置。 */
175
+ /**
176
+ * OpenAPI JSON endpoint 配置。
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * import type { ServOpenAPIHttpConfig } from '@h-ai/serv'
181
+ * import { serv } from '@h-ai/serv'
182
+ *
183
+ * const openapi: ServOpenAPIHttpConfig = { path: '/openapi.json' }
184
+ * // 文档仅在 createApp.http 显式启用时挂载。
185
+ * ```
186
+ */
95
187
  interface ServOpenAPIHttpConfig {
96
188
  readonly path: `/${string}`;
97
189
  }
98
- /** 文档页面 endpoint 配置。 */
190
+ /**
191
+ * 文档页面 endpoint 配置。
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * import type { ServDocsHttpConfig } from '@h-ai/serv'
196
+ * import { serv } from '@h-ai/serv'
197
+ *
198
+ * const docs: ServDocsHttpConfig = { path: '/docs', requireAuth: true }
199
+ * ```
200
+ */
99
201
  interface ServDocsHttpConfig {
100
202
  readonly path: `/${string}`;
101
203
  readonly requireAuth?: boolean;
102
204
  }
103
- /** 健康检查 endpoint 配置。 */
205
+ /**
206
+ * 健康检查 endpoint 配置。
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * import type { ServHealthHttpConfig } from '@h-ai/serv'
211
+ * import { serv } from '@h-ai/serv'
212
+ *
213
+ * const health: ServHealthHttpConfig = { path: '/health', readyPath: '/ready' }
214
+ * ```
215
+ */
104
216
  interface ServHealthHttpConfig {
105
217
  readonly path: `/${string}`;
106
218
  readonly readyPath?: `/${string}`;
107
219
  }
108
- /** 内部 RPC endpoint 配置。 */
220
+ /**
221
+ * 内部 RPC endpoint 配置。
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * import type { ServRpcHttpConfig } from '@h-ai/serv'
226
+ * import { serv } from '@h-ai/serv'
227
+ *
228
+ * const rpc: ServRpcHttpConfig = { prefix: '/rpc', access: 'loopback' }
229
+ * // 缺少可信连接信息时拒绝;跨节点来源通过明确网关/网络策略控制。
230
+ * ```
231
+ */
109
232
  interface ServRpcHttpConfig {
110
233
  readonly prefix: `/${string}`;
111
234
  readonly access: 'loopback' | 'private-network' | 'gateway-only';
112
235
  readonly gatewayHeader?: string;
113
236
  readonly gatewaySecret?: string;
114
237
  }
115
- /** 传输加密运行时配置(对应 `config/_serv.yml`)。 */
238
+ /**
239
+ * 传输加密运行时配置(对应 `config/_serv.yml`)。
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * import type { ServTransportRuntimeConfig } from '@h-ai/serv'
244
+ * import { serv } from '@h-ai/serv'
245
+ *
246
+ * const transport: ServTransportRuntimeConfig = { keyExchangePath: '/_hai/key-exchange', excludePaths: ['/health'] }
247
+ * // 仅路径配置;真正启用还需私有 keyring 与共享 keyStore。
248
+ * ```
249
+ */
116
250
  interface ServTransportRuntimeConfig {
117
251
  readonly keyExchangePath: `/${string}`;
118
252
  readonly excludePaths: readonly `/${string}`[];
119
- readonly maxClients: number;
120
253
  }
121
- /** `config/_serv.yml` 中可序列化的跨端 CORS 配置。 */
254
+ /**
255
+ * `config/_serv.yml` 中可序列化的跨端 CORS 配置。
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * import type { ServCorsRuntimeConfig } from '@h-ai/serv'
260
+ * import { serv } from '@h-ai/serv'
261
+ *
262
+ * const cors: ServCorsRuntimeConfig = { origin: 'https://app.example.com', allowedHeaders: ['Authorization', 'Content-Type'], exposedHeaders: [], credentials: true }
263
+ * ```
264
+ */
122
265
  interface ServCorsRuntimeConfig {
123
266
  readonly origin?: string;
124
267
  readonly nativeOrigins?: string;
@@ -126,7 +269,17 @@ interface ServCorsRuntimeConfig {
126
269
  readonly exposedHeaders: readonly string[];
127
270
  readonly credentials: boolean;
128
271
  }
129
- /** ServHttpApp 的 HTTP 挂载配置。 */
272
+ /**
273
+ * ServHttpApp 的 HTTP 挂载配置。
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * import type { ServHttpConfig } from '@h-ai/serv'
278
+ * import { serv } from '@h-ai/serv'
279
+ *
280
+ * const http: ServHttpConfig = { trustedProxies: [], apiPrefix: '/api/v1', openapi: false, docs: false, health: { path: '/health' }, rpc: false }
281
+ * ```
282
+ */
130
283
  interface ServHttpConfig {
131
284
  /** 可信任的连接代理 IP/CIDR;默认空数组,不采纳转发头。 */
132
285
  readonly trustedProxies: readonly string[];
@@ -136,22 +289,65 @@ interface ServHttpConfig {
136
289
  readonly health: false | ServHealthHttpConfig;
137
290
  readonly rpc: false | ServRpcHttpConfig;
138
291
  }
139
- /** `config/_serv.yml` 的顶层配置结构。 */
292
+ /**
293
+ * `config/_serv.yml` 的顶层配置结构。
294
+ *
295
+ * @example
296
+ * ```ts
297
+ * import type { ServConfig } from '@h-ai/serv'
298
+ * import { serv } from '@h-ai/serv'
299
+ *
300
+ * import { ServConfigSchema } from '@h-ai/serv'
301
+ *
302
+ * const config: ServConfig = ServConfigSchema.parse({ server: { host: '127.0.0.1', port: 3000 } })
303
+ * const port = config.server.port
304
+ * ```
305
+ */
140
306
  interface ServConfig {
141
307
  readonly server: ServServerConfig;
142
308
  readonly http: ServHttpConfig;
143
309
  readonly cors: ServCorsRuntimeConfig;
144
310
  readonly transport: false | ServTransportRuntimeConfig;
145
311
  }
146
- /** `config/_serv.yml` 中的 CORS 输入类型。 */
312
+ /**
313
+ * `config/_serv.yml` 中的 CORS 输入类型。
314
+ *
315
+ * @example
316
+ * ```ts
317
+ * import type { ServCorsRuntimeConfigInput } from '@h-ai/serv'
318
+ * import { serv } from '@h-ai/serv'
319
+ *
320
+ * const input: ServCorsRuntimeConfigInput = { origin: 'https://app.example.com', credentials: true }
321
+ * ```
322
+ */
147
323
  type ServCorsRuntimeConfigInput = Partial<ServCorsRuntimeConfig>;
148
- /** `config/_serv.yml` 中的传输加密输入类型。 */
324
+ /**
325
+ * `config/_serv.yml` 中的传输加密输入类型。
326
+ *
327
+ * @example
328
+ * ```ts
329
+ * import type { ServTransportRuntimeConfigInput } from '@h-ai/serv'
330
+ * import { serv } from '@h-ai/serv'
331
+ *
332
+ * const input: ServTransportRuntimeConfigInput = { keyExchangePath: '/_hai/key-exchange' }
333
+ * // 此输入不包含私钥;私有 Secret 仅在服务端运行时装配。
334
+ * ```
335
+ */
149
336
  type ServTransportRuntimeConfigInput = Partial<{
150
337
  readonly keyExchangePath: `/${string}`;
151
338
  readonly excludePaths: readonly `/${string}`[];
152
- readonly maxClients: number;
153
339
  }>;
154
- /** `serv.createApp` 接受的 HTTP 配置入参类型。 */
340
+ /**
341
+ * `serv.createApp` 接受的 HTTP 配置入参类型。
342
+ *
343
+ * @example
344
+ * ```ts
345
+ * import type { ServHttpConfigInput } from '@h-ai/serv'
346
+ * import { serv } from '@h-ai/serv'
347
+ *
348
+ * const http: ServHttpConfigInput = { apiPrefix: '/api/v2', trustedProxies: ['192.0.2.0/24'], docs: false }
349
+ * ```
350
+ */
155
351
  type ServHttpConfigInput = Partial<{
156
352
  readonly trustedProxies: readonly string[];
157
353
  readonly apiPrefix: `/api/${string}`;
@@ -160,14 +356,40 @@ type ServHttpConfigInput = Partial<{
160
356
  readonly health: false | Partial<ServHealthHttpConfig>;
161
357
  readonly rpc: false | Partial<ServRpcHttpConfig>;
162
358
  }>;
163
- /** `config/_serv.yml` 的顶层输入类型。 */
359
+ /**
360
+ * `config/_serv.yml` 的顶层输入类型。
361
+ *
362
+ * @example
363
+ * ```ts
364
+ * import type { ServConfigInput } from '@h-ai/serv'
365
+ * import { serv } from '@h-ai/serv'
366
+ *
367
+ * import { ServConfigSchema } from '@h-ai/serv'
368
+ *
369
+ * const input: ServConfigInput = { http: { apiPrefix: '/api/v2' }, transport: false }
370
+ * const parsed = ServConfigSchema.safeParse(input)
371
+ * if (!parsed.success) throw new Error('Invalid service configuration')
372
+ * ```
373
+ */
164
374
  interface ServConfigInput {
165
375
  readonly server?: ServServerConfigInput;
166
376
  readonly http?: ServHttpConfigInput;
167
377
  readonly cors?: ServCorsRuntimeConfigInput;
168
378
  readonly transport?: false | ServTransportRuntimeConfigInput;
169
379
  }
170
- /** `config/_serv.yml` 对应的配置 Schema。 */
380
+ /**
381
+ * `config/_serv.yml` 对应的配置 Schema。
382
+ *
383
+ * @example
384
+ * ```ts
385
+ * import { ServConfigSchema } from '@h-ai/serv'
386
+ *
387
+ * const parsed = ServConfigSchema.safeParse({ http: { apiPrefix: '/api/v1', docs: false } })
388
+ * if (parsed.success) {
389
+ * const host = parsed.data.server.host
390
+ * }
391
+ * ```
392
+ */
171
393
  declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
172
394
  server: z.ZodOptional<z.ZodObject<{
173
395
  host: z.ZodDefault<z.ZodString>;
@@ -208,7 +430,6 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
208
430
  transport: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
209
431
  keyExchangePath: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
210
432
  excludePaths: z.ZodDefault<z.ZodArray<z.ZodCustom<`/${string}`, `/${string}`>>>;
211
- maxClients: z.ZodDefault<z.ZodNumber>;
212
433
  }, z.core.$strip>]>>;
213
434
  }, z.core.$strip>, z.ZodTransform<{
214
435
  server: ServServerConfig;
@@ -251,7 +472,6 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
251
472
  transport?: false | {
252
473
  keyExchangePath: `/${string}`;
253
474
  excludePaths: `/${string}`[];
254
- maxClients: number;
255
475
  } | undefined;
256
476
  }>>;
257
477
 
@@ -273,8 +493,25 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
273
493
  * @module serv-transport
274
494
  */
275
495
 
276
- /** {@link createApp} 的 `transport` 配置项。 */
496
+ /**
497
+ * {@link createApp} 的 `transport` 配置项。
498
+ *
499
+ * @example
500
+ * ```ts
501
+ * import type { ServTransportConfig } from '@h-ai/serv'
502
+ * import { serv } from '@h-ai/serv'
503
+ *
504
+ * import { crypto } from '@h-ai/crypto'
505
+ *
506
+ * function configure(serverKeyRing: ServTransportConfig['serverKeyRing'], keyStore: ServTransportConfig['keyStore']): ServTransportConfig {
507
+ * return { crypto, serverKeyRing, keyStore }
508
+ * }
509
+ * // 私有 keyring 跨节点共享并支持轮换;生产 keyStore 原子消费重放标识,不能用进程 Map。
510
+ * ```
511
+ */
277
512
  interface ServTransportConfig {
513
+ /** 受管服务端密钥环,仅运行时从私有 Secret 注入。 */
514
+ readonly serverKeyRing: TransportServerKeyRing;
278
515
  /**
279
516
  * crypto 服务实例。serv 通过 `crypto.transport.createServer()` 创建
280
517
  * 传输加密管理器,并使用 `crypto.transport.protocol` 中的协议常量。
@@ -290,9 +527,7 @@ interface ServTransportConfig {
290
527
  */
291
528
  readonly excludePaths?: readonly string[];
292
529
  /** 共享客户端公钥存储;适用于多节点部署。 */
293
- readonly keyStore?: TransportKeyStore;
294
- /** 服务端可缓存的客户端公钥数量上限。默认 10000。 */
295
- readonly maxClients?: number;
530
+ readonly keyStore: TransportKeyStore;
296
531
  }
297
532
 
298
533
  /**
@@ -319,6 +554,15 @@ interface ServTransportConfig {
319
554
  * - `path` 省略时默认挂到 `'*'`
320
555
  * - 按数组顺序注册
321
556
  * - 注册位置固定在内置 `securityHeaders` 之后、`transport` / 业务路由之前
557
+ *
558
+ * @example
559
+ * ```ts
560
+ * import type { ServMiddlewareMount } from '@h-ai/serv'
561
+ * import { serv } from '@h-ai/serv'
562
+ *
563
+ * const mount: ServMiddlewareMount = { path: '/api/v1/*', middleware: serv.cors({ origin: 'https://app.example.com' }) }
564
+ * // middleware 在 transport 之前运行;短路响应不会经过传输加密。
565
+ * ```
322
566
  */
323
567
  interface ServMiddlewareMount {
324
568
  readonly path?: string;
@@ -330,6 +574,17 @@ interface ServMiddlewareMount {
330
574
  * Hono 是当前内部实现细节,应用代码只应依赖此最小接口:
331
575
  * - `fetch`:部署到 Fetch-first 运行时或交给 `serv.listen()`。
332
576
  * - `request`:测试 / 本地直连时发起 in-process 请求,避免消费方 import Hono。
577
+ *
578
+ * @example
579
+ * ```ts
580
+ * import type { ServHttpApp } from '@h-ai/serv'
581
+ * import { serv } from '@h-ai/serv'
582
+ *
583
+ * async function health(app: ServHttpApp): Promise<boolean> {
584
+ * const response = await app.request('/health')
585
+ * return response.ok
586
+ * }
587
+ * ```
333
588
  */
334
589
  interface ServHttpApp {
335
590
  readonly fetch: (request: Request, metadata?: ServRequestMetadata) => Response | Promise<Response>;
@@ -347,10 +602,20 @@ interface ServHttpApp {
347
602
  * - **`refreshCookie`**(可选):浏览器默认用 httpOnly cookie;受信原生客户端可配置 body 通道。与 `iam` 正交:
348
603
  * `iam` 决定 *谁* 验证 token,`refreshCookie` 决定 refresh token *怎么* 传输。
349
604
  *
350
- * - **`verifyToken`**(高级):不使用 IAM 模块的逆脱口。优先于 `iam.auth.verifyToken`。
605
+ * - **`verifyToken`**(高级):不使用 IAM 模块的覆盖入口。优先于 `iam.auth.verifyToken`。
351
606
  *
352
607
  * - **`createContext`**(高级):完全接管上下文构造,设置后 serv **不再**自动填充 session,
353
608
  * 由调用方负责。
609
+ *
610
+ * @example
611
+ * ```ts
612
+ * import type { CreateServAppOptions } from '@h-ai/serv'
613
+ * import { serv } from '@h-ai/serv'
614
+ *
615
+ * function mount<TContract extends Parameters<typeof serv.createApp>[0]['contract'], TProcedures extends Parameters<typeof serv.createApp>[0]['procedures']>(options: CreateServAppOptions<TContract, TProcedures>) {
616
+ * return serv.createApp(options)
617
+ * }
618
+ * ```
354
619
  */
355
620
  interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>> {
356
621
  readonly contract: TContract;
@@ -389,14 +654,10 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
389
654
  readonly createContext?: CreateServContext;
390
655
  /**
391
656
  * 启用传输加密(opt-in)。提供后 serv 自动:
392
- * 1. 调用 `crypto.transport.createServer()` 创建管理器
657
+ * 1. 使用显式 serverKeyRing / keyStore 创建受管传输管理器
393
658
  * 2. 在 oRPC 路由之前挂载中间件,自动解密请求 / 加密响应
394
659
  * 3. 在 `${apiPrefix}${keyExchangePath}` 暴露 POST 密钥协商端点
395
660
  *
396
- * @example
397
- * ```ts
398
- * serv.createApp({ contract, procedures, transport: { crypto } })
399
- * ```
400
661
  */
401
662
  readonly transport?: ServTransportConfig;
402
663
  /**
@@ -407,14 +668,25 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
407
668
  *
408
669
  * 需配合 `serv.listen()`(Node 适配器会自动完成 WebSocket 升级注入)。
409
670
  *
410
- * @example
411
- * ```ts
412
- * serv.createApp({ contract, procedures, iam, audio: { ai, verifyTicket, authorize } })
413
- * ```
414
671
  */
415
672
  readonly audio?: ServAudioConfig;
416
673
  }
417
- /** `serv.createApp()` 的语音 WebSocket 接入配置。 */
674
+ /**
675
+ * `serv.createApp()` 的语音 WebSocket 接入配置。
676
+ *
677
+ * @example
678
+ * ```ts
679
+ * import type { ServAudioConfig } from '@h-ai/serv'
680
+ * import { serv } from '@h-ai/serv'
681
+ *
682
+ * import { ai } from '@h-ai/ai'
683
+ *
684
+ * function configureAudio(verifyTicket: ServAudioConfig['verifyTicket']): ServAudioConfig {
685
+ * return { ai, verifyTicket, preAuthTimeoutMs: 3000, maxSessionMs: 60000 }
686
+ * }
687
+ * // 应用实现 verifyTicket:原子消费共享票据并返回可信 session/grant。
688
+ * ```
689
+ */
418
690
  interface ServAudioConfig {
419
691
  /** AI 服务对象(提供 `ai.audio` 领域能力)。 */
420
692
  readonly ai: AudioWsDeps['ai'];
@@ -453,7 +725,18 @@ interface ServAudioConfig {
453
725
  /** 发送缓冲高水位(字节,默认 8 MiB):`ws.bufferedAmount` 超过即以领域错误关闭慢客户端。 */
454
726
  readonly maxSendBufferBytes?: number;
455
727
  }
456
- /** Audio ticket 绑定的能力授权(消费时原样返回,供服务端交叉校验 start 帧)。 */
728
+ /**
729
+ * Audio ticket 绑定的能力授权(消费时原样返回,供服务端交叉校验 start 帧)。
730
+ *
731
+ * @example
732
+ * ```ts
733
+ * import type { AudioTicketGrant } from '@h-ai/serv'
734
+ * import { serv } from '@h-ai/serv'
735
+ *
736
+ * const grant: AudioTicketGrant = { operation: 'synthesize', model: 'speech-model', sessionId: 'session-1' }
737
+ * // 签发时绑定;start 帧不能覆盖服务端核准的模型与操作。
738
+ * ```
739
+ */
457
740
  interface AudioTicketGrant {
458
741
  /** 允许的操作 */
459
742
  readonly operation?: 'transcribe' | 'synthesize';
@@ -464,14 +747,38 @@ interface AudioTicketGrant {
464
747
  /** 应用自定义绑定字段 */
465
748
  readonly [key: string]: unknown;
466
749
  }
467
- /** Audio ticket 验证结果:IAM 会话 + 可选能力授权。 */
750
+ /**
751
+ * Audio ticket 验证结果:IAM 会话 + 可选能力授权。
752
+ *
753
+ * @example
754
+ * ```ts
755
+ * import type { AudioTicketVerification } from '@h-ai/serv'
756
+ * import { serv } from '@h-ai/serv'
757
+ *
758
+ * function verified(session: AudioTicketVerification['session']): AudioTicketVerification {
759
+ * return { session, grant: { operation: 'transcribe' } }
760
+ * }
761
+ * // session 必须来自成功消费的可信票据,不能从客户端 JSON 直接构造。
762
+ * ```
763
+ */
468
764
  interface AudioTicketVerification {
469
765
  /** IAM 会话摘要 */
470
766
  readonly session: ServSession;
471
767
  /** 票据绑定的能力授权(如有) */
472
768
  readonly grant?: AudioTicketGrant;
473
769
  }
474
- /** Audio WebSocket 经应用授权后允许生效的服务端参数。 */
770
+ /**
771
+ * Audio WebSocket 经应用授权后允许生效的服务端参数。
772
+ *
773
+ * @example
774
+ * ```ts
775
+ * import type { AuthorizedAudioRequest } from '@h-ai/serv'
776
+ * import { serv } from '@h-ai/serv'
777
+ *
778
+ * const request: AuthorizedAudioRequest = { operation: 'synthesize', format: 'mp3' }
779
+ * // model/voice/instruction 只有经应用 authorize 确认后才生效。
780
+ * ```
781
+ */
475
782
  interface AuthorizedAudioRequest {
476
783
  readonly operation: 'transcribe' | 'synthesize';
477
784
  /** 服务端允许的模型 ID;不传时使用 `ai.audio` 对应操作的默认模型。 */
@@ -482,23 +789,24 @@ interface AuthorizedAudioRequest {
482
789
  readonly sampleRate?: number;
483
790
  }
484
791
  /**
485
- * 创建并装配 HTTP API app。
792
+ * 创建并装配 HTTP app;同步校验配置和依赖,返回可交给 Node 或标准 fetch 适配器的应用。
486
793
  *
487
- * @param options - contract/procedures/http 三段式配置
488
- * @returns 可被 `serv.listen()` / `serv.toFetch()` 使用的 ServHttpApp
794
+ * @param options - contract、procedures、HTTP、安全和可选能力依赖
795
+ * @returns 不拥有业务模块生命周期的 ServHttpApp
796
+ * @throws 配置或必需依赖缺失时在装配阶段抛错,宿主须停止启动并清理已初始化资源。
489
797
  *
490
798
  * @example
491
799
  * ```ts
492
- * const app = serv.createApp({
493
- * contract,
494
- * procedures,
495
- * http: {
496
- * apiPrefix: '/api/v1',
497
- * openapi: { path: '/openapi.json' },
498
- * docs: { path: '/docs' },
499
- * },
500
- * })
501
- * serv.listen(app, { port: 3000, host: '0.0.0.0' })
800
+ * import type { ServContext } from '@h-ai/serv'
801
+ * import { apiContract } from '@h-ai/api-contract'
802
+ * import { serv } from '@h-ai/serv'
803
+ *
804
+ * const contract = apiContract.create({})
805
+ * const procedures = serv.implement(contract).context<ServContext>().build()
806
+ * const app = serv.createApp({ contract, procedures })
807
+ *
808
+ * const response = await app.request('/health')
809
+ * const healthy = response.ok
502
810
  * ```
503
811
  */
504
812
  declare function createApp<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>>(options: CreateServAppOptions<TContract, TProcedures>): ServHttpApp;
@@ -507,35 +815,49 @@ declare function createApp<TContract extends AnyContractRouter, TProcedures exte
507
815
  * @h-ai/serv — Fetch 适配器
508
816
  *
509
817
  * 将 ServHttpApp 包装为标准 Web `fetch(Request)` 处理函数,
510
- * 适用于 Cloudflare Workers、Deno、Bun 等 Fetch-first 运行时。
818
+ * 用于 Node 兼容宿主与客户端测试的标准 fetch 注入;部署仍须满足包的 Node API 依赖。
511
819
  * @module adapters/serv-adapter-fetch
512
820
  */
513
821
 
514
822
  /**
515
- * Fetch runtime handler。
823
+ * 标准 fetch 签名类型,供 Node 兼容宿主或客户端 in-process 测试注入。签名兼容不代表包整体不依赖 Node API,也不承诺未经验证的边缘部署。
516
824
  *
517
- * 兼容标准 Web `fetch` 签名 `(input, init)` —— 既可作为 Cloudflare Workers / Bun
518
- * 的 `fetch` 导出,也可作为 oRPC client `fetch` 选项的替代实现,让客户端在测试
519
- * 中直连 in-process 服务。
825
+ * @example
826
+ * ```ts
827
+ * import type { ServFetchHandler } from '@h-ai/serv'
828
+ * import { serv } from '@h-ai/serv'
829
+ *
830
+ * function handle(app: Parameters<typeof serv.toFetch>[0]): ServFetchHandler {
831
+ * return serv.toFetch(app)
832
+ * }
833
+ * // 兼容 fetch 签名;宿主仍须支持此包的 Node 依赖。
834
+ * ```
520
835
  */
521
836
  type ServFetchHandler = typeof fetch;
522
837
  /**
523
- * 将 ServHttpApp 转成 Fetch handler。
838
+ * 将应用转换为标准 fetch 签名,供 Node 兼容宿主或客户端测试注入;签名适配不代表整个包可在无 Node API 的边缘运行时加载。
524
839
  *
525
- * @param app - `serv.createApp()` 返回的 HTTP app
526
- * @returns fetch-compatible handler
840
+ * RequestInit 的覆盖与正文锁定/消费检查交由标准 Request 构造器处理,不修改原 Request 的字段。
841
+ * 取消会拒绝响应等待并传递给应用,不能撤回 handler 已产生的外部效果;响应交付后的流取消由应用消费 signal。
842
+ *
843
+ * @param app - createApp 返回的应用
844
+ * @returns fetch 兼容处理器
845
+ * @throws 返回的 Promise 可因 Request 构造错误、取消或下游异常而 reject;成功返回 Response。
527
846
  *
528
847
  * @example
529
848
  * ```ts
530
- * // Cloudflare Workers
531
- * export default { fetch: serv.toFetch(app) }
849
+ * import type { ServContext } from '@h-ai/serv'
850
+ * import { apiContract } from '@h-ai/api-contract'
851
+ * import { serv } from '@h-ai/serv'
532
852
  *
533
- * // Bun
534
- * Bun.serve({ fetch: serv.toFetch(app), port: 3000 })
853
+ * const contract = apiContract.create({})
854
+ * const procedures = serv.implement(contract).context<ServContext>().build()
855
+ * const app = serv.createApp({ contract, procedures })
535
856
  *
536
- * // 测试:把服务端 app 当作 fetch 注入到 client,免去 HTTP 监听
537
- * const client = apiClient.create(contract)
538
- * await client.init({ baseUrl: 'http://test', fetch: serv.toFetch(app) })
857
+ * const fetchHandler = serv.toFetch(app)
858
+ * const request = new Request('http://service.test/health')
859
+ * const response = await fetchHandler(request, { headers: { 'x-hai-locale': 'en-US' } })
860
+ * const ready = response.ok
539
861
  * ```
540
862
  */
541
863
  declare function toFetch(app: ServHttpApp): ServFetchHandler;
@@ -548,7 +870,18 @@ declare function toFetch(app: ServHttpApp): ServFetchHandler;
548
870
  * @module adapters/serv-adapter-node
549
871
  */
550
872
 
551
- /** Node 服务器启动配置。 */
873
+ /**
874
+ * Node 服务器启动配置。
875
+ *
876
+ * @example
877
+ * ```ts
878
+ * import type { ServListenOptions } from '@h-ai/serv'
879
+ * import { serv } from '@h-ai/serv'
880
+ *
881
+ * const options: ServListenOptions = { host: '127.0.0.1', port: 3000 }
882
+ * // HOST/PORT 与配置文件的解析由应用显式完成后传入。
883
+ * ```
884
+ */
552
885
  interface ServListenOptions {
553
886
  /**
554
887
  * 监听端口。
@@ -578,7 +911,20 @@ interface ServListenOptions {
578
911
  */
579
912
  readonly onClose?: () => Promise<void>;
580
913
  }
581
- /** Node 服务运行句柄。 */
914
+ /**
915
+ * Node 服务运行句柄。
916
+ *
917
+ * @example
918
+ * ```ts
919
+ * import type { ServNodeServer } from '@h-ai/serv'
920
+ * import { serv } from '@h-ai/serv'
921
+ *
922
+ * async function stop(handle: ServNodeServer) {
923
+ * await handle.close()
924
+ * }
925
+ * // close 只关闭监听句柄;业务资源由宿主生命周期关闭。
926
+ * ```
927
+ */
582
928
  interface ServNodeServer {
583
929
  /** 底层 Node.js HTTP Server 实例。 */
584
930
  readonly server: ServerType;
@@ -594,7 +940,7 @@ interface ServNodeServer {
594
940
  * 在 Node.js 中启动 ServHttpApp。
595
941
  *
596
942
  * 默认监听 `127.0.0.1:3000`;host/port 建议由应用从 `serv` 配置的 `server`
597
- *(支持 `HOST` / `PORT` 环境变量覆盖,环境变量高于配置文件)解析后显式传入。
943
+ * (支持 `HOST` / `PORT` 环境变量覆盖,环境变量高于配置文件)解析后显式传入。
598
944
  * 传入 `onClose` 后自动注册 SIGINT/SIGTERM 优雅关闭,应用层无需手动处理信号。
599
945
  *
600
946
  * @param app - `serv.createApp()` 返回的 HTTP app
@@ -603,14 +949,16 @@ interface ServNodeServer {
603
949
  *
604
950
  * @example
605
951
  * ```ts
606
- * // host/port 来自已解析的 serv 配置(env > 配置文件 > 默认)
607
- * const { server } = core.config.getOrThrow<ServConfig>('serv')
608
- * serv.listen(app, {
609
- * host: server.host,
610
- * port: server.port,
611
- * onListening: info => logger.info('listening', { port: info.port }),
612
- * onClose: closeApp,
613
- * })
952
+ * import type { ServContext } from '@h-ai/serv'
953
+ * import { apiContract } from '@h-ai/api-contract'
954
+ * import { serv } from '@h-ai/serv'
955
+ *
956
+ * const contract = apiContract.create({})
957
+ * const procedures = serv.implement(contract).context<ServContext>().build()
958
+ * const app = serv.createApp({ contract, procedures })
959
+ *
960
+ * const server = serv.listen(app, { host: '127.0.0.1', port: 3000 })
961
+ * await server.close()
614
962
  * ```
615
963
  */
616
964
  declare function listen(app: ServHttpApp, options?: ServListenOptions): ServNodeServer;
@@ -622,7 +970,18 @@ declare function listen(app: ServHttpApp, options?: ServListenOptions): ServNode
622
970
  * @module serv-cors
623
971
  */
624
972
 
625
- /** serv CORS 配置。 */
973
+ /**
974
+ * serv CORS 配置。
975
+ *
976
+ * @example
977
+ * ```ts
978
+ * import type { ServCorsConfig } from '@h-ai/serv'
979
+ * import { serv } from '@h-ai/serv'
980
+ *
981
+ * const config: ServCorsConfig = { origin: 'https://app.example.com', credentials: true, exposedHeaders: ['X-Request-Id'] }
982
+ * const middleware = serv.cors(config)
983
+ * ```
984
+ */
626
985
  interface ServCorsConfig {
627
986
  /** 允许的 origin:`'*'`、单个字符串、数组或自定义匹配函数。默认 `'*'`。 */
628
987
  readonly origin?: '*' | string | readonly string[] | ((origin: string) => boolean);
@@ -637,7 +996,17 @@ interface ServCorsConfig {
637
996
  /** 预检缓存秒数。默认 `86400`。 */
638
997
  readonly maxAge?: number;
639
998
  }
640
- /** 创建可复用的 CORS middleware。 */
999
+ /**
1000
+ * 创建可复用的 CORS middleware。
1001
+ *
1002
+ * @example
1003
+ * ```ts
1004
+ * import { serv } from '@h-ai/serv'
1005
+ *
1006
+ * const middleware = serv.cors({ origin: ['https://app.example.com'], credentials: true })
1007
+ * // 生产策略由 createRuntimeSecurityPolicy 先收敛 Origin,避免误用通配符。
1008
+ * ```
1009
+ */
641
1010
  declare function cors(config?: ServCorsConfig): ServMiddleware;
642
1011
 
643
1012
  /**
@@ -648,7 +1017,17 @@ declare function cors(config?: ServCorsConfig): ServMiddleware;
648
1017
  * @module serv-openapi
649
1018
  */
650
1019
 
651
- /** OpenAPI spec 生成配置。 */
1020
+ /**
1021
+ * OpenAPI spec 生成配置。
1022
+ *
1023
+ * @example
1024
+ * ```ts
1025
+ * import type { GenerateOpenAPISpecOptions } from '@h-ai/serv'
1026
+ * import { serv } from '@h-ai/serv'
1027
+ *
1028
+ * const options: GenerateOpenAPISpecOptions = { title: 'Reports API', version: '1.0.0', apiPrefix: '/api/v1' }
1029
+ * ```
1030
+ */
652
1031
  interface GenerateOpenAPISpecOptions {
653
1032
  readonly title?: string;
654
1033
  readonly version?: string;
@@ -656,117 +1035,60 @@ interface GenerateOpenAPISpecOptions {
656
1035
  readonly description?: string;
657
1036
  }
658
1037
  /**
659
- * 由应用级 contract 生成 OpenAPI 3.1 spec。
1038
+ * 由真实应用 contract 生成 OpenAPI 3.1 文档。
660
1039
  *
661
- * @param contract - 已组合完成的应用级 oRPC contract
662
- * @param options - 文档元信息
663
- * @returns OpenAPI 文档对象
1040
+ * @param contract - 应用 contract
1041
+ * @param options - 标题、版本、描述与服务前缀
1042
+ * @returns OpenAPI 文档
1043
+ * @throws 底层转换器不能处理该 Schema 时 reject,调用方在启动或导出边界处理。
664
1044
  *
665
1045
  * @example
666
1046
  * ```ts
667
- * const spec = await serv.generateSpec(contract, {
668
- * title: 'My API',
669
- * version: '1.0.0',
670
- * apiPrefix: '/api/v1',
671
- * })
1047
+ * import { apiContract } from '@h-ai/api-contract'
1048
+ * import { serv } from '@h-ai/serv'
1049
+ *
1050
+ * const spec = await serv.generateSpec(apiContract.create({}), { title: 'Reports API', version: '1.0.0', apiPrefix: '/api/v1' })
1051
+ * const title = spec.info.title
672
1052
  * ```
673
1053
  */
674
1054
  declare function generateSpec(contract: AnyContractRouter, options?: GenerateOpenAPISpecOptions): Promise<OpenAPI.Document>;
675
1055
 
676
1056
  /**
677
- * @h-ai/serv — contract-first 链式 procedure 实现器
678
- *
679
- * 在 oRPC `implement()` 之上提供 route 名只出现一次的链式 DSL。
680
- * 应用只接触 contract 路径、上下文与 guard,不需要组装 oRPC router 对象。
681
- *
682
- * @module serv-router
683
- */
684
-
685
- type EmptyContext = Record<never, never>;
686
- type StringKey<T> = Extract<keyof T, string>;
687
- type ServContract = ContractRouter<Meta>;
688
- /** contract 中所有 procedure 的点分路径。 */
689
- type ServRoutePath<TContract extends ServContract> = TContract extends ContractProcedure<infer _TInputSchema, infer _TOutputSchema, infer _TErrorMap, infer _TMeta> ? never : {
690
- [K in StringKey<TContract>]: TContract[K] extends ContractProcedure<infer _TChildInputSchema, infer _TChildOutputSchema, infer _TChildErrorMap, infer _TChildMeta> ? K : TContract[K] extends ServContract ? ServRoutePath<TContract[K]> extends infer TChild extends string ? `${K}.${TChild}` : never : never;
691
- }[StringKey<TContract>];
692
- type ContractAtPath<TContract, TPath extends string> = TPath extends `${infer THead}.${infer TTail}` ? THead extends keyof TContract ? ContractAtPath<TContract[THead], TTail> : never : TPath extends keyof TContract ? TContract[TPath] : never;
693
- type RouteContract<TContract extends ServContract, TPath extends ServRoutePath<TContract>> = ContractAtPath<TContract, TPath>;
694
- /** `.auth()`、`.permission()`、`.role()` 后的非空会话上下文。 */
695
- type AuthenticatedServContext<TContext extends ServContext = ServContext> = TContext & {
696
- readonly session: NonNullable<TContext['session']>;
697
- };
698
- type RouteHandler<TRouteContract, TContext extends Context> = TRouteContract extends ContractProcedure<infer TInputSchema, infer TOutputSchema, infer TErrorMap, infer TMeta> ? InferSchemaInput<TOutputSchema> extends HaiResult<unknown> ? ProcedureHandler<TContext, InferSchemaOutput<TInputSchema>, InferSchemaInput<TOutputSchema>, TErrorMap, TMeta> : never : never;
699
- /** 从 contract procedure 精确推导当前 route 的 HaiResult handler。 */
700
- type ServRouteHandler<TContract extends ServContract, TPath extends ServRoutePath<TContract>, TContext extends Context> = RouteHandler<RouteContract<TContract, TPath>, TContext>;
701
- type ServGuardedRouteHandler<TContract extends ServContract, TPath extends ServRoutePath<TContract>, TContext extends ServContext> = RouteHandler<RouteContract<TContract, TPath>, AuthenticatedServContext<TContext>>;
702
- type NextBuilder<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract>, TPath extends TRemaining> = ServRouterBuilder<TContract, TInitialContext, TCurrentContext, Exclude<TRemaining, TPath>>;
703
- /** 尚未声明 guard 的单 route 注册器。 */
704
- interface ServRouteRegistration<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract>, TPath extends TRemaining> {
705
- readonly auth: () => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
706
- readonly permission: (permission: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
707
- readonly role: (role: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
708
- }
709
- /** 已声明认证或授权 guard 的单 route 注册器。 */
710
- interface ServGuardedRouteRegistration<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract>, TPath extends TRemaining> {
711
- readonly auth: () => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
712
- readonly permission: (permission: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
713
- readonly role: (role: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
714
- readonly handle: (handler: ServGuardedRouteHandler<TContract, TPath, TCurrentContext>) => NextBuilder<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
715
- }
716
- interface ServRouteMethod<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract>> {
717
- <TPath extends TRemaining>(path: TPath, handler: ServRouteHandler<TContract, TPath, TCurrentContext>): NextBuilder<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
718
- <TPath extends TRemaining>(path: TPath): ServRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
719
- }
720
- type ServRouterBuilderBuild<TContract extends ServContract, TInitialContext extends Context, TRemaining> = [TRemaining] extends [never] ? {
721
- /** 同步返回完整的普通 router,允许按需选用嵌套的默认 procedures。 */
722
- readonly build: () => UnlaziedRouter<Router<TContract, TInitialContext>>;
723
- } : {
724
- /** 仅供 TypeScript/IDE 展示尚未实现的 route;运行时不存在。 */
725
- readonly missingRoutes: TRemaining;
726
- };
727
- /**
728
- * 链式 router builder。
1057
+ * @h-ai/serv — 部署环境 HTTP 安全策略
729
1058
  *
730
- * `TRemaining` 会随着 `.route()` 注册逐步收窄;未实现完整 contract 时没有 `.build()`。
1059
+ * 将环境与 CORS 配置收敛为 fail-closed 的运行时策略,避免每个业务应用重复实现
1060
+ * Origin 解析、生产通配符保护、Cookie Secure 与 API 文档暴露规则。
1061
+ * @module serv-runtime-security
731
1062
  */
732
- type ServRouterBuilder<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract> = ServRoutePath<TContract>> = {
733
- readonly route: ServRouteMethod<TContract, TInitialContext, TCurrentContext, TRemaining>;
734
- } & ServRouterBuilderBuild<TContract, TInitialContext, TRemaining>;
735
- /** hai-framework 链式 contract 实现器。 */
736
- interface ServImplementer<TContract extends ServContract> {
737
- readonly context: <TContext extends ServContext>() => ServRouterBuilder<TContract, TContext & EmptyContext, TContext>;
738
- }
739
1063
  /**
740
- * 创建链式 contract 实现器。
1064
+ * 运行时安全策略输入;通常来自 `_core.yml.env` 与 `_serv.yml.cors`。
741
1065
  *
742
1066
  * @example
743
1067
  * ```ts
744
- * serv
745
- * .implement(contract)
746
- * .context<ServContext>()
747
- * .route('info', infoHandler)
748
- * .route('echo')
749
- * .auth()
750
- * .handle(echoHandler)
751
- * .build()
752
- * ```
753
- */
754
- declare function implement<TContract extends ServContract>(contract: TContract): ServImplementer<TContract>;
755
-
756
- /**
757
- * @h-ai/serv — 部署环境 HTTP 安全策略
1068
+ * import type { ServRuntimeSecurityPolicyInput } from '@h-ai/serv'
1069
+ * import { serv } from '@h-ai/serv'
758
1070
  *
759
- * 将环境与 CORS 配置收敛为 fail-closed 的运行时策略,避免每个业务应用重复实现
760
- * Origin 解析、生产通配符保护、Cookie Secure 与 API 文档暴露规则。
761
- * @module serv-runtime-security
1071
+ * const input: ServRuntimeSecurityPolicyInput = { environment: 'production', corsOrigin: 'https://app.example.com' }
1072
+ * const policy = serv.createRuntimeSecurityPolicy(input)
1073
+ * ```
762
1074
  */
763
- /** 运行时安全策略输入;通常来自 `_core.yml.env` 与 `_serv.yml.cors`。 */
764
1075
  interface ServRuntimeSecurityPolicyInput {
765
1076
  readonly environment?: string;
766
1077
  readonly corsOrigin?: string;
767
1078
  readonly nativeOrigins?: string;
768
1079
  }
769
- /** 可直接装配到 CORS、refresh cookie 与 HTTP 文档配置的安全策略。 */
1080
+ /**
1081
+ * 可直接装配到 CORS、refresh cookie 与 HTTP 文档配置的安全策略。
1082
+ *
1083
+ * @example
1084
+ * ```ts
1085
+ * import type { ServRuntimeSecurityPolicy } from '@h-ai/serv'
1086
+ * import { serv } from '@h-ai/serv'
1087
+ *
1088
+ * const policy: ServRuntimeSecurityPolicy = serv.createRuntimeSecurityPolicy({ environment: 'production', corsOrigin: 'https://app.example.com' })
1089
+ * const allowed = policy.allowOrigin('https://app.example.com')
1090
+ * ```
1091
+ */
770
1092
  interface ServRuntimeSecurityPolicy {
771
1093
  readonly allowOrigin: (origin: string) => boolean;
772
1094
  readonly isNativeOrigin: (origin: string) => boolean;
@@ -774,9 +1096,19 @@ interface ServRuntimeSecurityPolicy {
774
1096
  readonly exposeApiDocs: boolean;
775
1097
  }
776
1098
  /**
777
- * 创建部署环境安全策略。
1099
+ * 校验部署 Origin 并生成 Cookie、文档与跨端 CORS 安全策略。
1100
+ *
1101
+ * @param input - 部署环境与显式允许的 Web/原生 Origin
1102
+ * @returns 可用于 HTTP 装配的策略
1103
+ * @throws Origin 非法,或 production 缺少明确 Web Origin/使用通配符时抛配置错误。
778
1104
  *
779
- * 生产环境必须显式配置 HTTP(S) Origin,禁止缺省或 `*`;开发环境保留通配符便利性。
1105
+ * @example
1106
+ * ```ts
1107
+ * import { serv } from '@h-ai/serv'
1108
+ *
1109
+ * const policy = serv.createRuntimeSecurityPolicy({ environment: 'production', corsOrigin: 'https://app.example.com' })
1110
+ * const middleware = serv.cors({ origin: policy.allowOrigin, credentials: true })
1111
+ * ```
780
1112
  */
781
1113
  declare function createRuntimeSecurityPolicy(input: ServRuntimeSecurityPolicyInput): ServRuntimeSecurityPolicy;
782
1114
 
@@ -788,11 +1120,32 @@ declare function createRuntimeSecurityPolicy(input: ServRuntimeSecurityPolicyInp
788
1120
  * @module serv-storage-assets
789
1121
  */
790
1122
 
791
- /** 可由只读资源 middleware 使用的最小 Storage 能力。 */
1123
+ /**
1124
+ * 可由只读资源 middleware 使用的最小 Storage 能力。
1125
+ *
1126
+ * @example
1127
+ * ```ts
1128
+ * // 模块内部上下文:示例复用当前声明所在模块的实际绑定。
1129
+ * function withStorage(source: StorageAssetSource): ServStorageAssetsConfig {
1130
+ * return { storage: source, pathPrefix: '/assets/', keyPattern: new RegExp('^public/[a-z0-9-]+[.]webp$'), allowedContentTypes: ['image/webp'] }
1131
+ * }
1132
+ * ```
1133
+ */
792
1134
  interface StorageAssetSource {
793
1135
  readonly file: Pick<StorageFunctions['file'], 'get' | 'head'>;
794
1136
  }
795
- /** Storage 只读资源 middleware 配置。 */
1137
+ /**
1138
+ * Storage 只读资源 middleware 配置。
1139
+ *
1140
+ * @example
1141
+ * ```ts
1142
+ * import { serv } from '@h-ai/serv'
1143
+ * import { storage } from '@h-ai/storage'
1144
+ *
1145
+ * // 仅发布明确公共资源;私有业务资源另执行可信资源授权。
1146
+ * const middleware = serv.storageAssets({ storage, pathPrefix: '/assets/', keyPattern: new RegExp('^public/[a-z0-9-]+[.]webp$'), allowedContentTypes: ['image/webp'], cacheControl: 'public, max-age=3600' })
1147
+ * ```
1148
+ */
796
1149
  interface ServStorageAssetsConfig {
797
1150
  /** 已初始化的 Storage 模块或等价文件读取能力。 */
798
1151
  readonly storage: StorageAssetSource;
@@ -818,15 +1171,14 @@ interface ServStorageAssetsConfig {
818
1171
  *
819
1172
  * @param config - Storage、路径、key、MIME 与缓存策略。
820
1173
  * @returns 可挂载到 `serv.createApp({ middlewares })` 的 middleware。
1174
+ *
821
1175
  * @example
822
1176
  * ```ts
823
- * const middleware = serv.storageAssets({
824
- * storage,
825
- * pathPrefix: '/api/v1/avatar-assets/',
826
- * keyPattern: /^avatars\/[\w-]+\/[\w-]+\.webp$/,
827
- * allowedContentTypes: ['image/webp'],
828
- * cacheControl: 'public, max-age=31536000, immutable',
829
- * })
1177
+ * import { serv } from '@h-ai/serv'
1178
+ * import { storage } from '@h-ai/storage'
1179
+ *
1180
+ * // 仅发布明确公共资源;私有业务资源另执行可信资源授权。
1181
+ * const middleware = serv.storageAssets({ storage, pathPrefix: '/assets/', keyPattern: new RegExp('^public/[a-z0-9-]+[.]webp$'), allowedContentTypes: ['image/webp'], cacheControl: 'public, max-age=3600' })
830
1182
  * ```
831
1183
  */
832
1184
  declare function storageAssets(config: ServStorageAssetsConfig): ServMiddleware;
@@ -848,6 +1200,17 @@ declare function storageAssets(config: ServStorageAssetsConfig): ServMiddleware;
848
1200
  * 复用 `HaiResult` 失败分支结构。字段错误同时在两处出现:
849
1201
  * - `error.cause`:与 `validateInputOrFail` 输出对齐,HaiResult 标准位置
850
1202
  * - 顶层 `errors`:保留以兼容已有客户端
1203
+ *
1204
+ * @example
1205
+ * ```ts
1206
+ * import type { ServValidationFailureBody } from '@h-ai/serv'
1207
+ * import { serv } from '@h-ai/serv'
1208
+ *
1209
+ * function fields(body: ServValidationFailureBody) {
1210
+ * return body.error.cause ?? body.errors
1211
+ * }
1212
+ * // 业务调用先判断 success,字段错误仅包含安全的本地化文案。
1213
+ * ```
851
1214
  */
852
1215
  interface ServValidationFailureBody {
853
1216
  readonly success: false;
@@ -869,6 +1232,14 @@ interface ServValidationFailureBody {
869
1232
  *
870
1233
  * @param headers - 请求头(`Headers` 或与之等价的对象)
871
1234
  * @returns 解析得到的 locale
1235
+ *
1236
+ * @example
1237
+ * ```ts
1238
+ * import { serv } from '@h-ai/serv'
1239
+ *
1240
+ * const locale = serv.resolveRequestLocale(new Headers({ 'x-hai-locale': 'en-US' }))
1241
+ * const fallback = serv.resolveRequestLocale(new Headers())
1242
+ * ```
872
1243
  */
873
1244
  declare function resolveRequestLocale(headers: Headers | Record<string, string | undefined>): string;
874
1245
  /**
@@ -898,10 +1269,39 @@ declare function resolveRequestLocale(headers: Headers | Record<string, string |
898
1269
  * @param schema - Zod schema
899
1270
  * @param input - 待校验数据
900
1271
  * @param locale - 期望 locale(通常来自请求上下文)
1272
+ *
1273
+ * @example
1274
+ * ```ts
1275
+ * import { serv } from '@h-ai/serv'
1276
+ * import { z } from 'zod'
1277
+ *
1278
+ * const result = serv.validateInputOrFail(z.object({ name: z.string().min(1) }), { name: '' }, 'en-US')
1279
+ * if (!result.success) {
1280
+ * const fields = result.error.cause
1281
+ * } else {
1282
+ * const name = result.data.name
1283
+ * }
1284
+ * ```
901
1285
  */
902
1286
  declare function validateInputOrFail<TOutput>(schema: ZodType<TOutput>, input: unknown, locale?: string): HaiResult<TOutput>;
903
1287
 
904
- /** hai-framework API service runtime 统一入口(扁平 API)。 */
1288
+ /**
1289
+ * hai-framework API service runtime 统一入口(扁平 API)。
1290
+ *
1291
+ * @example
1292
+ * ```ts
1293
+ * import type { ServContext } from '@h-ai/serv'
1294
+ * import { apiContract } from '@h-ai/api-contract'
1295
+ * import { serv } from '@h-ai/serv'
1296
+ *
1297
+ * const contract = apiContract.create({})
1298
+ * const procedures = serv.implement(contract).context<ServContext>().build()
1299
+ * const app = serv.createApp({ contract, procedures })
1300
+ *
1301
+ * const response = await app.request('/health')
1302
+ * if (!response.ok) throw new Error('Health request failed')
1303
+ * ```
1304
+ */
905
1305
  declare const serv: {
906
1306
  createApp: typeof createApp;
907
1307
  parseRequestContext: typeof parseRequestContext;
@@ -920,4 +1320,4 @@ declare const serv: {
920
1320
  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;
921
1321
  };
922
1322
 
923
- export { type AudioTicketGrant, type AudioTicketVerification, type AuthenticatedServContext, type AuthorizedAudioRequest, type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServAudioConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServCorsRuntimeConfig, type ServCorsRuntimeConfigInput, type ServDocsHttpConfig, type ServFetchHandler, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServImplementer, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, ServRequestMetadata, type ServRouteHandler, type ServRoutePath, type ServRouterBuilder, type ServRpcHttpConfig, type ServRuntimeSecurityPolicy, type ServRuntimeSecurityPolicyInput, type ServServerConfig, type ServServerConfigInput, ServSession, type ServStorageAssetsConfig, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };
1323
+ export { type AudioTicketGrant, type AudioTicketVerification, type AuthorizedAudioRequest, type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServAudioConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServCorsRuntimeConfig, type ServCorsRuntimeConfigInput, type ServDocsHttpConfig, type ServFetchHandler, 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, ServRequestMetadata, type ServRpcHttpConfig, type ServRuntimeSecurityPolicy, type ServRuntimeSecurityPolicyInput, type ServServerConfig, type ServServerConfigInput, ServSession, type ServStorageAssetsConfig, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };