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

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-B-AxU8hj.js';
7
+ export { e as CreateServContextInput, N as NativeRefreshTokenTransportConfig, f as RefreshTokenPair } from './serv-context-B-AxU8hj.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-C8dSz2k-.js';
14
+ export { A as AuthenticatedServContext, S as ServImplementer, a as ServRouteHandler, b as ServRoutePath, c as ServRouterBuilder } from './serv-router-C8dSz2k-.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,127 @@ 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 {
251
+ /** 双向明文 UTF-8 字节上限;省略使用 1 MiB,范围 1 KiB~64 MiB。 */
252
+ readonly maxPlaintextBytes?: number;
117
253
  readonly keyExchangePath: `/${string}`;
118
254
  readonly excludePaths: readonly `/${string}`[];
119
- readonly maxClients: number;
120
255
  }
121
- /** `config/_serv.yml` 中可序列化的跨端 CORS 配置。 */
256
+ /**
257
+ * `config/_serv.yml` 中可序列化的跨端 CORS 配置。
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * import type { ServCorsRuntimeConfig } from '@h-ai/serv'
262
+ * import { serv } from '@h-ai/serv'
263
+ *
264
+ * const cors: ServCorsRuntimeConfig = { origin: 'https://app.example.com', allowedHeaders: ['Authorization', 'Content-Type'], exposedHeaders: [], credentials: true }
265
+ * ```
266
+ */
122
267
  interface ServCorsRuntimeConfig {
123
268
  readonly origin?: string;
124
269
  readonly nativeOrigins?: string;
@@ -126,7 +271,17 @@ interface ServCorsRuntimeConfig {
126
271
  readonly exposedHeaders: readonly string[];
127
272
  readonly credentials: boolean;
128
273
  }
129
- /** ServHttpApp 的 HTTP 挂载配置。 */
274
+ /**
275
+ * ServHttpApp 的 HTTP 挂载配置。
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * import type { ServHttpConfig } from '@h-ai/serv'
280
+ * import { serv } from '@h-ai/serv'
281
+ *
282
+ * const http: ServHttpConfig = { trustedProxies: [], apiPrefix: '/api/v1', openapi: false, docs: false, health: { path: '/health' }, rpc: false }
283
+ * ```
284
+ */
130
285
  interface ServHttpConfig {
131
286
  /** 可信任的连接代理 IP/CIDR;默认空数组,不采纳转发头。 */
132
287
  readonly trustedProxies: readonly string[];
@@ -136,22 +291,66 @@ interface ServHttpConfig {
136
291
  readonly health: false | ServHealthHttpConfig;
137
292
  readonly rpc: false | ServRpcHttpConfig;
138
293
  }
139
- /** `config/_serv.yml` 的顶层配置结构。 */
294
+ /**
295
+ * `config/_serv.yml` 的顶层配置结构。
296
+ *
297
+ * @example
298
+ * ```ts
299
+ * import type { ServConfig } from '@h-ai/serv'
300
+ * import { serv } from '@h-ai/serv'
301
+ *
302
+ * import { ServConfigSchema } from '@h-ai/serv'
303
+ *
304
+ * const config: ServConfig = ServConfigSchema.parse({ server: { host: '127.0.0.1', port: 3000 } })
305
+ * const port = config.server.port
306
+ * ```
307
+ */
140
308
  interface ServConfig {
141
309
  readonly server: ServServerConfig;
142
310
  readonly http: ServHttpConfig;
143
311
  readonly cors: ServCorsRuntimeConfig;
144
312
  readonly transport: false | ServTransportRuntimeConfig;
145
313
  }
146
- /** `config/_serv.yml` 中的 CORS 输入类型。 */
314
+ /**
315
+ * `config/_serv.yml` 中的 CORS 输入类型。
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * import type { ServCorsRuntimeConfigInput } from '@h-ai/serv'
320
+ * import { serv } from '@h-ai/serv'
321
+ *
322
+ * const input: ServCorsRuntimeConfigInput = { origin: 'https://app.example.com', credentials: true }
323
+ * ```
324
+ */
147
325
  type ServCorsRuntimeConfigInput = Partial<ServCorsRuntimeConfig>;
148
- /** `config/_serv.yml` 中的传输加密输入类型。 */
326
+ /**
327
+ * `config/_serv.yml` 中的传输加密输入类型。
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * import type { ServTransportRuntimeConfigInput } from '@h-ai/serv'
332
+ * import { serv } from '@h-ai/serv'
333
+ *
334
+ * const input: ServTransportRuntimeConfigInput = { keyExchangePath: '/_hai/key-exchange' }
335
+ * // 此输入不包含私钥;私有 Secret 仅在服务端运行时装配。
336
+ * ```
337
+ */
149
338
  type ServTransportRuntimeConfigInput = Partial<{
339
+ readonly maxPlaintextBytes: number;
150
340
  readonly keyExchangePath: `/${string}`;
151
341
  readonly excludePaths: readonly `/${string}`[];
152
- readonly maxClients: number;
153
342
  }>;
154
- /** `serv.createApp` 接受的 HTTP 配置入参类型。 */
343
+ /**
344
+ * `serv.createApp` 接受的 HTTP 配置入参类型。
345
+ *
346
+ * @example
347
+ * ```ts
348
+ * import type { ServHttpConfigInput } from '@h-ai/serv'
349
+ * import { serv } from '@h-ai/serv'
350
+ *
351
+ * const http: ServHttpConfigInput = { apiPrefix: '/api/v2', trustedProxies: ['192.0.2.0/24'], docs: false }
352
+ * ```
353
+ */
155
354
  type ServHttpConfigInput = Partial<{
156
355
  readonly trustedProxies: readonly string[];
157
356
  readonly apiPrefix: `/api/${string}`;
@@ -160,14 +359,40 @@ type ServHttpConfigInput = Partial<{
160
359
  readonly health: false | Partial<ServHealthHttpConfig>;
161
360
  readonly rpc: false | Partial<ServRpcHttpConfig>;
162
361
  }>;
163
- /** `config/_serv.yml` 的顶层输入类型。 */
362
+ /**
363
+ * `config/_serv.yml` 的顶层输入类型。
364
+ *
365
+ * @example
366
+ * ```ts
367
+ * import type { ServConfigInput } from '@h-ai/serv'
368
+ * import { serv } from '@h-ai/serv'
369
+ *
370
+ * import { ServConfigSchema } from '@h-ai/serv'
371
+ *
372
+ * const input: ServConfigInput = { http: { apiPrefix: '/api/v2' }, transport: false }
373
+ * const parsed = ServConfigSchema.safeParse(input)
374
+ * if (!parsed.success) throw new Error('Invalid service configuration')
375
+ * ```
376
+ */
164
377
  interface ServConfigInput {
165
378
  readonly server?: ServServerConfigInput;
166
379
  readonly http?: ServHttpConfigInput;
167
380
  readonly cors?: ServCorsRuntimeConfigInput;
168
381
  readonly transport?: false | ServTransportRuntimeConfigInput;
169
382
  }
170
- /** `config/_serv.yml` 对应的配置 Schema。 */
383
+ /**
384
+ * `config/_serv.yml` 对应的配置 Schema。
385
+ *
386
+ * @example
387
+ * ```ts
388
+ * import { ServConfigSchema } from '@h-ai/serv'
389
+ *
390
+ * const parsed = ServConfigSchema.safeParse({ http: { apiPrefix: '/api/v1', docs: false } })
391
+ * if (parsed.success) {
392
+ * const host = parsed.data.server.host
393
+ * }
394
+ * ```
395
+ */
171
396
  declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
172
397
  server: z.ZodOptional<z.ZodObject<{
173
398
  host: z.ZodDefault<z.ZodString>;
@@ -206,9 +431,9 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
206
431
  credentials: z.ZodDefault<z.ZodBoolean>;
207
432
  }, z.core.$strip>>;
208
433
  transport: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
434
+ maxPlaintextBytes: z.ZodOptional<z.ZodNumber>;
209
435
  keyExchangePath: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
210
436
  excludePaths: z.ZodDefault<z.ZodArray<z.ZodCustom<`/${string}`, `/${string}`>>>;
211
- maxClients: z.ZodDefault<z.ZodNumber>;
212
437
  }, z.core.$strip>]>>;
213
438
  }, z.core.$strip>, z.ZodTransform<{
214
439
  server: ServServerConfig;
@@ -251,7 +476,7 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
251
476
  transport?: false | {
252
477
  keyExchangePath: `/${string}`;
253
478
  excludePaths: `/${string}`[];
254
- maxClients: number;
479
+ maxPlaintextBytes?: number | undefined;
255
480
  } | undefined;
256
481
  }>>;
257
482
 
@@ -273,8 +498,27 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
273
498
  * @module serv-transport
274
499
  */
275
500
 
276
- /** {@link createApp} 的 `transport` 配置项。 */
501
+ /**
502
+ * {@link createApp} 的 `transport` 配置项。
503
+ *
504
+ * @example
505
+ * ```ts
506
+ * import type { ServTransportConfig } from '@h-ai/serv'
507
+ * import { serv } from '@h-ai/serv'
508
+ *
509
+ * import { crypto } from '@h-ai/crypto'
510
+ *
511
+ * function configure(serverKeyRing: ServTransportConfig['serverKeyRing'], keyStore: ServTransportConfig['keyStore']): ServTransportConfig {
512
+ * return { crypto, serverKeyRing, keyStore }
513
+ * }
514
+ * // 私有 keyring 跨节点共享并支持轮换;生产 keyStore 原子消费重放标识,不能用进程 Map。
515
+ * ```
516
+ */
277
517
  interface ServTransportConfig {
518
+ /** 受管服务端密钥环,仅运行时从私有 Secret 注入。 */
519
+ /** 双向业务明文 UTF-8 字节上限;默认 1 MiB,整数范围 1 KiB~64 MiB。 */
520
+ readonly maxPlaintextBytes?: number;
521
+ readonly serverKeyRing: TransportServerKeyRing;
278
522
  /**
279
523
  * crypto 服务实例。serv 通过 `crypto.transport.createServer()` 创建
280
524
  * 传输加密管理器,并使用 `crypto.transport.protocol` 中的协议常量。
@@ -290,9 +534,7 @@ interface ServTransportConfig {
290
534
  */
291
535
  readonly excludePaths?: readonly string[];
292
536
  /** 共享客户端公钥存储;适用于多节点部署。 */
293
- readonly keyStore?: TransportKeyStore;
294
- /** 服务端可缓存的客户端公钥数量上限。默认 10000。 */
295
- readonly maxClients?: number;
537
+ readonly keyStore: TransportKeyStore;
296
538
  }
297
539
 
298
540
  /**
@@ -319,6 +561,15 @@ interface ServTransportConfig {
319
561
  * - `path` 省略时默认挂到 `'*'`
320
562
  * - 按数组顺序注册
321
563
  * - 注册位置固定在内置 `securityHeaders` 之后、`transport` / 业务路由之前
564
+ *
565
+ * @example
566
+ * ```ts
567
+ * import type { ServMiddlewareMount } from '@h-ai/serv'
568
+ * import { serv } from '@h-ai/serv'
569
+ *
570
+ * const mount: ServMiddlewareMount = { path: '/api/v1/*', middleware: serv.cors({ origin: 'https://app.example.com' }) }
571
+ * // middleware 在 transport 之前运行;短路响应不会经过传输加密。
572
+ * ```
322
573
  */
323
574
  interface ServMiddlewareMount {
324
575
  readonly path?: string;
@@ -330,6 +581,17 @@ interface ServMiddlewareMount {
330
581
  * Hono 是当前内部实现细节,应用代码只应依赖此最小接口:
331
582
  * - `fetch`:部署到 Fetch-first 运行时或交给 `serv.listen()`。
332
583
  * - `request`:测试 / 本地直连时发起 in-process 请求,避免消费方 import Hono。
584
+ *
585
+ * @example
586
+ * ```ts
587
+ * import type { ServHttpApp } from '@h-ai/serv'
588
+ * import { serv } from '@h-ai/serv'
589
+ *
590
+ * async function health(app: ServHttpApp): Promise<boolean> {
591
+ * const response = await app.request('/health')
592
+ * return response.ok
593
+ * }
594
+ * ```
333
595
  */
334
596
  interface ServHttpApp {
335
597
  readonly fetch: (request: Request, metadata?: ServRequestMetadata) => Response | Promise<Response>;
@@ -347,10 +609,20 @@ interface ServHttpApp {
347
609
  * - **`refreshCookie`**(可选):浏览器默认用 httpOnly cookie;受信原生客户端可配置 body 通道。与 `iam` 正交:
348
610
  * `iam` 决定 *谁* 验证 token,`refreshCookie` 决定 refresh token *怎么* 传输。
349
611
  *
350
- * - **`verifyToken`**(高级):不使用 IAM 模块的逆脱口。优先于 `iam.auth.verifyToken`。
612
+ * - **`verifyToken`**(高级):不使用 IAM 模块的覆盖入口。优先于 `iam.auth.verifyToken`。
351
613
  *
352
614
  * - **`createContext`**(高级):完全接管上下文构造,设置后 serv **不再**自动填充 session,
353
615
  * 由调用方负责。
616
+ *
617
+ * @example
618
+ * ```ts
619
+ * import type { CreateServAppOptions } from '@h-ai/serv'
620
+ * import { serv } from '@h-ai/serv'
621
+ *
622
+ * function mount<TContract extends Parameters<typeof serv.createApp>[0]['contract'], TProcedures extends Parameters<typeof serv.createApp>[0]['procedures']>(options: CreateServAppOptions<TContract, TProcedures>) {
623
+ * return serv.createApp(options)
624
+ * }
625
+ * ```
354
626
  */
355
627
  interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>> {
356
628
  readonly contract: TContract;
@@ -389,14 +661,10 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
389
661
  readonly createContext?: CreateServContext;
390
662
  /**
391
663
  * 启用传输加密(opt-in)。提供后 serv 自动:
392
- * 1. 调用 `crypto.transport.createServer()` 创建管理器
664
+ * 1. 使用显式 serverKeyRing / keyStore 创建受管传输管理器
393
665
  * 2. 在 oRPC 路由之前挂载中间件,自动解密请求 / 加密响应
394
666
  * 3. 在 `${apiPrefix}${keyExchangePath}` 暴露 POST 密钥协商端点
395
667
  *
396
- * @example
397
- * ```ts
398
- * serv.createApp({ contract, procedures, transport: { crypto } })
399
- * ```
400
668
  */
401
669
  readonly transport?: ServTransportConfig;
402
670
  /**
@@ -407,14 +675,25 @@ interface CreateServAppOptions<TContract extends AnyContractRouter, TProcedures
407
675
  *
408
676
  * 需配合 `serv.listen()`(Node 适配器会自动完成 WebSocket 升级注入)。
409
677
  *
410
- * @example
411
- * ```ts
412
- * serv.createApp({ contract, procedures, iam, audio: { ai, verifyTicket, authorize } })
413
- * ```
414
678
  */
415
679
  readonly audio?: ServAudioConfig;
416
680
  }
417
- /** `serv.createApp()` 的语音 WebSocket 接入配置。 */
681
+ /**
682
+ * `serv.createApp()` 的语音 WebSocket 接入配置。
683
+ *
684
+ * @example
685
+ * ```ts
686
+ * import type { ServAudioConfig } from '@h-ai/serv'
687
+ * import { serv } from '@h-ai/serv'
688
+ *
689
+ * import { ai } from '@h-ai/ai'
690
+ *
691
+ * function configureAudio(verifyTicket: ServAudioConfig['verifyTicket']): ServAudioConfig {
692
+ * return { ai, verifyTicket, preAuthTimeoutMs: 3000, maxSessionMs: 60000 }
693
+ * }
694
+ * // 应用实现 verifyTicket:原子消费共享票据并返回可信 session/grant。
695
+ * ```
696
+ */
418
697
  interface ServAudioConfig {
419
698
  /** AI 服务对象(提供 `ai.audio` 领域能力)。 */
420
699
  readonly ai: AudioWsDeps['ai'];
@@ -453,7 +732,18 @@ interface ServAudioConfig {
453
732
  /** 发送缓冲高水位(字节,默认 8 MiB):`ws.bufferedAmount` 超过即以领域错误关闭慢客户端。 */
454
733
  readonly maxSendBufferBytes?: number;
455
734
  }
456
- /** Audio ticket 绑定的能力授权(消费时原样返回,供服务端交叉校验 start 帧)。 */
735
+ /**
736
+ * Audio ticket 绑定的能力授权(消费时原样返回,供服务端交叉校验 start 帧)。
737
+ *
738
+ * @example
739
+ * ```ts
740
+ * import type { AudioTicketGrant } from '@h-ai/serv'
741
+ * import { serv } from '@h-ai/serv'
742
+ *
743
+ * const grant: AudioTicketGrant = { operation: 'synthesize', model: 'speech-model', sessionId: 'session-1' }
744
+ * // 签发时绑定;start 帧不能覆盖服务端核准的模型与操作。
745
+ * ```
746
+ */
457
747
  interface AudioTicketGrant {
458
748
  /** 允许的操作 */
459
749
  readonly operation?: 'transcribe' | 'synthesize';
@@ -464,14 +754,38 @@ interface AudioTicketGrant {
464
754
  /** 应用自定义绑定字段 */
465
755
  readonly [key: string]: unknown;
466
756
  }
467
- /** Audio ticket 验证结果:IAM 会话 + 可选能力授权。 */
757
+ /**
758
+ * Audio ticket 验证结果:IAM 会话 + 可选能力授权。
759
+ *
760
+ * @example
761
+ * ```ts
762
+ * import type { AudioTicketVerification } from '@h-ai/serv'
763
+ * import { serv } from '@h-ai/serv'
764
+ *
765
+ * function verified(session: AudioTicketVerification['session']): AudioTicketVerification {
766
+ * return { session, grant: { operation: 'transcribe' } }
767
+ * }
768
+ * // session 必须来自成功消费的可信票据,不能从客户端 JSON 直接构造。
769
+ * ```
770
+ */
468
771
  interface AudioTicketVerification {
469
772
  /** IAM 会话摘要 */
470
773
  readonly session: ServSession;
471
774
  /** 票据绑定的能力授权(如有) */
472
775
  readonly grant?: AudioTicketGrant;
473
776
  }
474
- /** Audio WebSocket 经应用授权后允许生效的服务端参数。 */
777
+ /**
778
+ * Audio WebSocket 经应用授权后允许生效的服务端参数。
779
+ *
780
+ * @example
781
+ * ```ts
782
+ * import type { AuthorizedAudioRequest } from '@h-ai/serv'
783
+ * import { serv } from '@h-ai/serv'
784
+ *
785
+ * const request: AuthorizedAudioRequest = { operation: 'synthesize', format: 'mp3' }
786
+ * // model/voice/instruction 只有经应用 authorize 确认后才生效。
787
+ * ```
788
+ */
475
789
  interface AuthorizedAudioRequest {
476
790
  readonly operation: 'transcribe' | 'synthesize';
477
791
  /** 服务端允许的模型 ID;不传时使用 `ai.audio` 对应操作的默认模型。 */
@@ -482,23 +796,24 @@ interface AuthorizedAudioRequest {
482
796
  readonly sampleRate?: number;
483
797
  }
484
798
  /**
485
- * 创建并装配 HTTP API app。
799
+ * 创建并装配 HTTP app;同步校验配置和依赖,返回可交给 Node 或标准 fetch 适配器的应用。
486
800
  *
487
- * @param options - contract/procedures/http 三段式配置
488
- * @returns 可被 `serv.listen()` / `serv.toFetch()` 使用的 ServHttpApp
801
+ * @param options - contract、procedures、HTTP、安全和可选能力依赖
802
+ * @returns 不拥有业务模块生命周期的 ServHttpApp
803
+ * @throws 配置或必需依赖缺失时在装配阶段抛错,宿主须停止启动并清理已初始化资源。
489
804
  *
490
805
  * @example
491
806
  * ```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' })
807
+ * import type { ServContext } from '@h-ai/serv'
808
+ * import { apiContract } from '@h-ai/api-contract'
809
+ * import { serv } from '@h-ai/serv'
810
+ *
811
+ * const contract = apiContract.create({})
812
+ * const procedures = serv.implement(contract).context<ServContext>().build()
813
+ * const app = serv.createApp({ contract, procedures })
814
+ *
815
+ * const response = await app.request('/health')
816
+ * const healthy = response.ok
502
817
  * ```
503
818
  */
504
819
  declare function createApp<TContract extends AnyContractRouter, TProcedures extends Router<AnyContractRouter, ServContext>>(options: CreateServAppOptions<TContract, TProcedures>): ServHttpApp;
@@ -507,35 +822,49 @@ declare function createApp<TContract extends AnyContractRouter, TProcedures exte
507
822
  * @h-ai/serv — Fetch 适配器
508
823
  *
509
824
  * 将 ServHttpApp 包装为标准 Web `fetch(Request)` 处理函数,
510
- * 适用于 Cloudflare Workers、Deno、Bun 等 Fetch-first 运行时。
825
+ * 用于 Node 兼容宿主与客户端测试的标准 fetch 注入;部署仍须满足包的 Node API 依赖。
511
826
  * @module adapters/serv-adapter-fetch
512
827
  */
513
828
 
514
829
  /**
515
- * Fetch runtime handler。
830
+ * 标准 fetch 签名类型,供 Node 兼容宿主或客户端 in-process 测试注入。签名兼容不代表包整体不依赖 Node API,也不承诺未经验证的边缘部署。
516
831
  *
517
- * 兼容标准 Web `fetch` 签名 `(input, init)` —— 既可作为 Cloudflare Workers / Bun
518
- * 的 `fetch` 导出,也可作为 oRPC client `fetch` 选项的替代实现,让客户端在测试
519
- * 中直连 in-process 服务。
832
+ * @example
833
+ * ```ts
834
+ * import type { ServFetchHandler } from '@h-ai/serv'
835
+ * import { serv } from '@h-ai/serv'
836
+ *
837
+ * function handle(app: Parameters<typeof serv.toFetch>[0]): ServFetchHandler {
838
+ * return serv.toFetch(app)
839
+ * }
840
+ * // 兼容 fetch 签名;宿主仍须支持此包的 Node 依赖。
841
+ * ```
520
842
  */
521
843
  type ServFetchHandler = typeof fetch;
522
844
  /**
523
- * 将 ServHttpApp 转成 Fetch handler。
845
+ * 将应用转换为标准 fetch 签名,供 Node 兼容宿主或客户端测试注入;签名适配不代表整个包可在无 Node API 的边缘运行时加载。
524
846
  *
525
- * @param app - `serv.createApp()` 返回的 HTTP app
526
- * @returns fetch-compatible handler
847
+ * RequestInit 的覆盖与正文锁定/消费检查交由标准 Request 构造器处理,不修改原 Request 的字段。
848
+ * 取消会拒绝响应等待并传递给应用,不能撤回 handler 已产生的外部效果;响应交付后的流取消由应用消费 signal。
849
+ *
850
+ * @param app - createApp 返回的应用
851
+ * @returns fetch 兼容处理器
852
+ * @throws 返回的 Promise 可因 Request 构造错误、取消或下游异常而 reject;成功返回 Response。
527
853
  *
528
854
  * @example
529
855
  * ```ts
530
- * // Cloudflare Workers
531
- * export default { fetch: serv.toFetch(app) }
856
+ * import type { ServContext } from '@h-ai/serv'
857
+ * import { apiContract } from '@h-ai/api-contract'
858
+ * import { serv } from '@h-ai/serv'
532
859
  *
533
- * // Bun
534
- * Bun.serve({ fetch: serv.toFetch(app), port: 3000 })
860
+ * const contract = apiContract.create({})
861
+ * const procedures = serv.implement(contract).context<ServContext>().build()
862
+ * const app = serv.createApp({ contract, procedures })
535
863
  *
536
- * // 测试:把服务端 app 当作 fetch 注入到 client,免去 HTTP 监听
537
- * const client = apiClient.create(contract)
538
- * await client.init({ baseUrl: 'http://test', fetch: serv.toFetch(app) })
864
+ * const fetchHandler = serv.toFetch(app)
865
+ * const request = new Request('http://service.test/health')
866
+ * const response = await fetchHandler(request, { headers: { 'x-hai-locale': 'en-US' } })
867
+ * const ready = response.ok
539
868
  * ```
540
869
  */
541
870
  declare function toFetch(app: ServHttpApp): ServFetchHandler;
@@ -548,7 +877,18 @@ declare function toFetch(app: ServHttpApp): ServFetchHandler;
548
877
  * @module adapters/serv-adapter-node
549
878
  */
550
879
 
551
- /** Node 服务器启动配置。 */
880
+ /**
881
+ * Node 服务器启动配置。
882
+ *
883
+ * @example
884
+ * ```ts
885
+ * import type { ServListenOptions } from '@h-ai/serv'
886
+ * import { serv } from '@h-ai/serv'
887
+ *
888
+ * const options: ServListenOptions = { host: '127.0.0.1', port: 3000 }
889
+ * // HOST/PORT 与配置文件的解析由应用显式完成后传入。
890
+ * ```
891
+ */
552
892
  interface ServListenOptions {
553
893
  /**
554
894
  * 监听端口。
@@ -578,7 +918,20 @@ interface ServListenOptions {
578
918
  */
579
919
  readonly onClose?: () => Promise<void>;
580
920
  }
581
- /** Node 服务运行句柄。 */
921
+ /**
922
+ * Node 服务运行句柄。
923
+ *
924
+ * @example
925
+ * ```ts
926
+ * import type { ServNodeServer } from '@h-ai/serv'
927
+ * import { serv } from '@h-ai/serv'
928
+ *
929
+ * async function stop(handle: ServNodeServer) {
930
+ * await handle.close()
931
+ * }
932
+ * // close 只关闭监听句柄;业务资源由宿主生命周期关闭。
933
+ * ```
934
+ */
582
935
  interface ServNodeServer {
583
936
  /** 底层 Node.js HTTP Server 实例。 */
584
937
  readonly server: ServerType;
@@ -594,7 +947,7 @@ interface ServNodeServer {
594
947
  * 在 Node.js 中启动 ServHttpApp。
595
948
  *
596
949
  * 默认监听 `127.0.0.1:3000`;host/port 建议由应用从 `serv` 配置的 `server`
597
- *(支持 `HOST` / `PORT` 环境变量覆盖,环境变量高于配置文件)解析后显式传入。
950
+ * (支持 `HOST` / `PORT` 环境变量覆盖,环境变量高于配置文件)解析后显式传入。
598
951
  * 传入 `onClose` 后自动注册 SIGINT/SIGTERM 优雅关闭,应用层无需手动处理信号。
599
952
  *
600
953
  * @param app - `serv.createApp()` 返回的 HTTP app
@@ -603,14 +956,16 @@ interface ServNodeServer {
603
956
  *
604
957
  * @example
605
958
  * ```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
- * })
959
+ * import type { ServContext } from '@h-ai/serv'
960
+ * import { apiContract } from '@h-ai/api-contract'
961
+ * import { serv } from '@h-ai/serv'
962
+ *
963
+ * const contract = apiContract.create({})
964
+ * const procedures = serv.implement(contract).context<ServContext>().build()
965
+ * const app = serv.createApp({ contract, procedures })
966
+ *
967
+ * const server = serv.listen(app, { host: '127.0.0.1', port: 3000 })
968
+ * await server.close()
614
969
  * ```
615
970
  */
616
971
  declare function listen(app: ServHttpApp, options?: ServListenOptions): ServNodeServer;
@@ -622,7 +977,18 @@ declare function listen(app: ServHttpApp, options?: ServListenOptions): ServNode
622
977
  * @module serv-cors
623
978
  */
624
979
 
625
- /** serv CORS 配置。 */
980
+ /**
981
+ * serv CORS 配置。
982
+ *
983
+ * @example
984
+ * ```ts
985
+ * import type { ServCorsConfig } from '@h-ai/serv'
986
+ * import { serv } from '@h-ai/serv'
987
+ *
988
+ * const config: ServCorsConfig = { origin: 'https://app.example.com', credentials: true, exposedHeaders: ['X-Request-Id'] }
989
+ * const middleware = serv.cors(config)
990
+ * ```
991
+ */
626
992
  interface ServCorsConfig {
627
993
  /** 允许的 origin:`'*'`、单个字符串、数组或自定义匹配函数。默认 `'*'`。 */
628
994
  readonly origin?: '*' | string | readonly string[] | ((origin: string) => boolean);
@@ -637,7 +1003,17 @@ interface ServCorsConfig {
637
1003
  /** 预检缓存秒数。默认 `86400`。 */
638
1004
  readonly maxAge?: number;
639
1005
  }
640
- /** 创建可复用的 CORS middleware。 */
1006
+ /**
1007
+ * 创建可复用的 CORS middleware。
1008
+ *
1009
+ * @example
1010
+ * ```ts
1011
+ * import { serv } from '@h-ai/serv'
1012
+ *
1013
+ * const middleware = serv.cors({ origin: ['https://app.example.com'], credentials: true })
1014
+ * // 生产策略由 createRuntimeSecurityPolicy 先收敛 Origin,避免误用通配符。
1015
+ * ```
1016
+ */
641
1017
  declare function cors(config?: ServCorsConfig): ServMiddleware;
642
1018
 
643
1019
  /**
@@ -648,7 +1024,17 @@ declare function cors(config?: ServCorsConfig): ServMiddleware;
648
1024
  * @module serv-openapi
649
1025
  */
650
1026
 
651
- /** OpenAPI spec 生成配置。 */
1027
+ /**
1028
+ * OpenAPI spec 生成配置。
1029
+ *
1030
+ * @example
1031
+ * ```ts
1032
+ * import type { GenerateOpenAPISpecOptions } from '@h-ai/serv'
1033
+ * import { serv } from '@h-ai/serv'
1034
+ *
1035
+ * const options: GenerateOpenAPISpecOptions = { title: 'Reports API', version: '1.0.0', apiPrefix: '/api/v1' }
1036
+ * ```
1037
+ */
652
1038
  interface GenerateOpenAPISpecOptions {
653
1039
  readonly title?: string;
654
1040
  readonly version?: string;
@@ -656,117 +1042,60 @@ interface GenerateOpenAPISpecOptions {
656
1042
  readonly description?: string;
657
1043
  }
658
1044
  /**
659
- * 由应用级 contract 生成 OpenAPI 3.1 spec。
1045
+ * 由真实应用 contract 生成 OpenAPI 3.1 文档。
660
1046
  *
661
- * @param contract - 已组合完成的应用级 oRPC contract
662
- * @param options - 文档元信息
663
- * @returns OpenAPI 文档对象
1047
+ * @param contract - 应用 contract
1048
+ * @param options - 标题、版本、描述与服务前缀
1049
+ * @returns OpenAPI 文档
1050
+ * @throws 底层转换器不能处理该 Schema 时 reject,调用方在启动或导出边界处理。
664
1051
  *
665
1052
  * @example
666
1053
  * ```ts
667
- * const spec = await serv.generateSpec(contract, {
668
- * title: 'My API',
669
- * version: '1.0.0',
670
- * apiPrefix: '/api/v1',
671
- * })
1054
+ * import { apiContract } from '@h-ai/api-contract'
1055
+ * import { serv } from '@h-ai/serv'
1056
+ *
1057
+ * const spec = await serv.generateSpec(apiContract.create({}), { title: 'Reports API', version: '1.0.0', apiPrefix: '/api/v1' })
1058
+ * const title = spec.info.title
672
1059
  * ```
673
1060
  */
674
1061
  declare function generateSpec(contract: AnyContractRouter, options?: GenerateOpenAPISpecOptions): Promise<OpenAPI.Document>;
675
1062
 
676
1063
  /**
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。
1064
+ * @h-ai/serv — 部署环境 HTTP 安全策略
729
1065
  *
730
- * `TRemaining` 会随着 `.route()` 注册逐步收窄;未实现完整 contract 时没有 `.build()`。
1066
+ * 将环境与 CORS 配置收敛为 fail-closed 的运行时策略,避免每个业务应用重复实现
1067
+ * Origin 解析、生产通配符保护、Cookie Secure 与 API 文档暴露规则。
1068
+ * @module serv-runtime-security
731
1069
  */
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
1070
  /**
740
- * 创建链式 contract 实现器。
1071
+ * 运行时安全策略输入;通常来自 `_core.yml.env` 与 `_serv.yml.cors`。
741
1072
  *
742
1073
  * @example
743
1074
  * ```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 安全策略
1075
+ * import type { ServRuntimeSecurityPolicyInput } from '@h-ai/serv'
1076
+ * import { serv } from '@h-ai/serv'
758
1077
  *
759
- * 将环境与 CORS 配置收敛为 fail-closed 的运行时策略,避免每个业务应用重复实现
760
- * Origin 解析、生产通配符保护、Cookie Secure 与 API 文档暴露规则。
761
- * @module serv-runtime-security
1078
+ * const input: ServRuntimeSecurityPolicyInput = { environment: 'production', corsOrigin: 'https://app.example.com' }
1079
+ * const policy = serv.createRuntimeSecurityPolicy(input)
1080
+ * ```
762
1081
  */
763
- /** 运行时安全策略输入;通常来自 `_core.yml.env` 与 `_serv.yml.cors`。 */
764
1082
  interface ServRuntimeSecurityPolicyInput {
765
1083
  readonly environment?: string;
766
1084
  readonly corsOrigin?: string;
767
1085
  readonly nativeOrigins?: string;
768
1086
  }
769
- /** 可直接装配到 CORS、refresh cookie 与 HTTP 文档配置的安全策略。 */
1087
+ /**
1088
+ * 可直接装配到 CORS、refresh cookie 与 HTTP 文档配置的安全策略。
1089
+ *
1090
+ * @example
1091
+ * ```ts
1092
+ * import type { ServRuntimeSecurityPolicy } from '@h-ai/serv'
1093
+ * import { serv } from '@h-ai/serv'
1094
+ *
1095
+ * const policy: ServRuntimeSecurityPolicy = serv.createRuntimeSecurityPolicy({ environment: 'production', corsOrigin: 'https://app.example.com' })
1096
+ * const allowed = policy.allowOrigin('https://app.example.com')
1097
+ * ```
1098
+ */
770
1099
  interface ServRuntimeSecurityPolicy {
771
1100
  readonly allowOrigin: (origin: string) => boolean;
772
1101
  readonly isNativeOrigin: (origin: string) => boolean;
@@ -774,9 +1103,19 @@ interface ServRuntimeSecurityPolicy {
774
1103
  readonly exposeApiDocs: boolean;
775
1104
  }
776
1105
  /**
777
- * 创建部署环境安全策略。
1106
+ * 校验部署 Origin 并生成 Cookie、文档与跨端 CORS 安全策略。
1107
+ *
1108
+ * @param input - 部署环境与显式允许的 Web/原生 Origin
1109
+ * @returns 可用于 HTTP 装配的策略
1110
+ * @throws Origin 非法,或 production 缺少明确 Web Origin/使用通配符时抛配置错误。
778
1111
  *
779
- * 生产环境必须显式配置 HTTP(S) Origin,禁止缺省或 `*`;开发环境保留通配符便利性。
1112
+ * @example
1113
+ * ```ts
1114
+ * import { serv } from '@h-ai/serv'
1115
+ *
1116
+ * const policy = serv.createRuntimeSecurityPolicy({ environment: 'production', corsOrigin: 'https://app.example.com' })
1117
+ * const middleware = serv.cors({ origin: policy.allowOrigin, credentials: true })
1118
+ * ```
780
1119
  */
781
1120
  declare function createRuntimeSecurityPolicy(input: ServRuntimeSecurityPolicyInput): ServRuntimeSecurityPolicy;
782
1121
 
@@ -788,11 +1127,32 @@ declare function createRuntimeSecurityPolicy(input: ServRuntimeSecurityPolicyInp
788
1127
  * @module serv-storage-assets
789
1128
  */
790
1129
 
791
- /** 可由只读资源 middleware 使用的最小 Storage 能力。 */
1130
+ /**
1131
+ * 可由只读资源 middleware 使用的最小 Storage 能力。
1132
+ *
1133
+ * @example
1134
+ * ```ts
1135
+ * // 模块内部上下文:示例复用当前声明所在模块的实际绑定。
1136
+ * function withStorage(source: StorageAssetSource): ServStorageAssetsConfig {
1137
+ * return { storage: source, pathPrefix: '/assets/', keyPattern: new RegExp('^public/[a-z0-9-]+[.]webp$'), allowedContentTypes: ['image/webp'] }
1138
+ * }
1139
+ * ```
1140
+ */
792
1141
  interface StorageAssetSource {
793
1142
  readonly file: Pick<StorageFunctions['file'], 'get' | 'head'>;
794
1143
  }
795
- /** Storage 只读资源 middleware 配置。 */
1144
+ /**
1145
+ * Storage 只读资源 middleware 配置。
1146
+ *
1147
+ * @example
1148
+ * ```ts
1149
+ * import { serv } from '@h-ai/serv'
1150
+ * import { storage } from '@h-ai/storage'
1151
+ *
1152
+ * // 仅发布明确公共资源;私有业务资源另执行可信资源授权。
1153
+ * const middleware = serv.storageAssets({ storage, pathPrefix: '/assets/', keyPattern: new RegExp('^public/[a-z0-9-]+[.]webp$'), allowedContentTypes: ['image/webp'], cacheControl: 'public, max-age=3600' })
1154
+ * ```
1155
+ */
796
1156
  interface ServStorageAssetsConfig {
797
1157
  /** 已初始化的 Storage 模块或等价文件读取能力。 */
798
1158
  readonly storage: StorageAssetSource;
@@ -818,15 +1178,14 @@ interface ServStorageAssetsConfig {
818
1178
  *
819
1179
  * @param config - Storage、路径、key、MIME 与缓存策略。
820
1180
  * @returns 可挂载到 `serv.createApp({ middlewares })` 的 middleware。
1181
+ *
821
1182
  * @example
822
1183
  * ```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
- * })
1184
+ * import { serv } from '@h-ai/serv'
1185
+ * import { storage } from '@h-ai/storage'
1186
+ *
1187
+ * // 仅发布明确公共资源;私有业务资源另执行可信资源授权。
1188
+ * const middleware = serv.storageAssets({ storage, pathPrefix: '/assets/', keyPattern: new RegExp('^public/[a-z0-9-]+[.]webp$'), allowedContentTypes: ['image/webp'], cacheControl: 'public, max-age=3600' })
830
1189
  * ```
831
1190
  */
832
1191
  declare function storageAssets(config: ServStorageAssetsConfig): ServMiddleware;
@@ -848,6 +1207,17 @@ declare function storageAssets(config: ServStorageAssetsConfig): ServMiddleware;
848
1207
  * 复用 `HaiResult` 失败分支结构。字段错误同时在两处出现:
849
1208
  * - `error.cause`:与 `validateInputOrFail` 输出对齐,HaiResult 标准位置
850
1209
  * - 顶层 `errors`:保留以兼容已有客户端
1210
+ *
1211
+ * @example
1212
+ * ```ts
1213
+ * import type { ServValidationFailureBody } from '@h-ai/serv'
1214
+ * import { serv } from '@h-ai/serv'
1215
+ *
1216
+ * function fields(body: ServValidationFailureBody) {
1217
+ * return body.error.cause ?? body.errors
1218
+ * }
1219
+ * // 业务调用先判断 success,字段错误仅包含安全的本地化文案。
1220
+ * ```
851
1221
  */
852
1222
  interface ServValidationFailureBody {
853
1223
  readonly success: false;
@@ -869,6 +1239,14 @@ interface ServValidationFailureBody {
869
1239
  *
870
1240
  * @param headers - 请求头(`Headers` 或与之等价的对象)
871
1241
  * @returns 解析得到的 locale
1242
+ *
1243
+ * @example
1244
+ * ```ts
1245
+ * import { serv } from '@h-ai/serv'
1246
+ *
1247
+ * const locale = serv.resolveRequestLocale(new Headers({ 'x-hai-locale': 'en-US' }))
1248
+ * const fallback = serv.resolveRequestLocale(new Headers())
1249
+ * ```
872
1250
  */
873
1251
  declare function resolveRequestLocale(headers: Headers | Record<string, string | undefined>): string;
874
1252
  /**
@@ -898,10 +1276,39 @@ declare function resolveRequestLocale(headers: Headers | Record<string, string |
898
1276
  * @param schema - Zod schema
899
1277
  * @param input - 待校验数据
900
1278
  * @param locale - 期望 locale(通常来自请求上下文)
1279
+ *
1280
+ * @example
1281
+ * ```ts
1282
+ * import { serv } from '@h-ai/serv'
1283
+ * import { z } from 'zod'
1284
+ *
1285
+ * const result = serv.validateInputOrFail(z.object({ name: z.string().min(1) }), { name: '' }, 'en-US')
1286
+ * if (!result.success) {
1287
+ * const fields = result.error.cause
1288
+ * } else {
1289
+ * const name = result.data.name
1290
+ * }
1291
+ * ```
901
1292
  */
902
1293
  declare function validateInputOrFail<TOutput>(schema: ZodType<TOutput>, input: unknown, locale?: string): HaiResult<TOutput>;
903
1294
 
904
- /** hai-framework API service runtime 统一入口(扁平 API)。 */
1295
+ /**
1296
+ * hai-framework API service runtime 统一入口(扁平 API)。
1297
+ *
1298
+ * @example
1299
+ * ```ts
1300
+ * import type { ServContext } from '@h-ai/serv'
1301
+ * import { apiContract } from '@h-ai/api-contract'
1302
+ * import { serv } from '@h-ai/serv'
1303
+ *
1304
+ * const contract = apiContract.create({})
1305
+ * const procedures = serv.implement(contract).context<ServContext>().build()
1306
+ * const app = serv.createApp({ contract, procedures })
1307
+ *
1308
+ * const response = await app.request('/health')
1309
+ * if (!response.ok) throw new Error('Health request failed')
1310
+ * ```
1311
+ */
905
1312
  declare const serv: {
906
1313
  createApp: typeof createApp;
907
1314
  parseRequestContext: typeof parseRequestContext;
@@ -920,4 +1327,4 @@ declare const serv: {
920
1327
  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
1328
  };
922
1329
 
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 };
1330
+ 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 };