@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/README.en.md +85 -19
- package/README.ja.md +85 -19
- package/README.md +86 -20
- package/dist/chunk-BGWE4S6Z.js +37 -0
- package/dist/chunk-BGWE4S6Z.js.map +1 -0
- package/dist/{chunk-R354OMWL.js → chunk-TWHCU6HT.js} +48 -6
- package/dist/chunk-TWHCU6HT.js.map +1 -0
- package/dist/{chunk-27PCPE37.js → chunk-XKCLD42I.js} +2 -2
- package/dist/chunk-XKCLD42I.js.map +1 -0
- package/dist/features/ai.d.ts +139 -57
- package/dist/features/ai.js +57 -46
- package/dist/features/ai.js.map +1 -1
- package/dist/features/feedback.d.ts +688 -0
- package/dist/features/feedback.js +82 -0
- package/dist/features/feedback.js.map +1 -0
- package/dist/features/iam.d.ts +29 -4
- package/dist/features/iam.js +3 -3
- package/dist/features/iam.js.map +1 -1
- package/dist/features/storage.d.ts +94 -9
- package/dist/features/storage.js +48 -12
- package/dist/features/storage.js.map +1 -1
- package/dist/index.d.ts +594 -187
- package/dist/index.js +109 -112
- package/dist/index.js.map +1 -1
- package/dist/{serv-context-CWzyrsdq.d.ts → serv-context-B-AxU8hj.d.ts} +129 -7
- package/dist/serv-router-C8dSz2k-.d.ts +322 -0
- package/package.json +24 -11
- package/dist/chunk-27PCPE37.js.map +0 -1
- package/dist/chunk-R354OMWL.js.map +0 -1
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
|
|
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-
|
|
7
|
-
export { e as CreateServContextInput, N as NativeRefreshTokenTransportConfig, f as RefreshTokenPair } from './serv-context-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
799
|
+
* 创建并装配 HTTP app;同步校验配置和依赖,返回可交给 Node 或标准 fetch 适配器的应用。
|
|
486
800
|
*
|
|
487
|
-
* @param options - contract
|
|
488
|
-
* @returns
|
|
801
|
+
* @param options - contract、procedures、HTTP、安全和可选能力依赖
|
|
802
|
+
* @returns 不拥有业务模块生命周期的 ServHttpApp
|
|
803
|
+
* @throws 配置或必需依赖缺失时在装配阶段抛错,宿主须停止启动并清理已初始化资源。
|
|
489
804
|
*
|
|
490
805
|
* @example
|
|
491
806
|
* ```ts
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
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
|
-
*
|
|
825
|
+
* 用于 Node 兼容宿主与客户端测试的标准 fetch 注入;部署仍须满足包的 Node API 依赖。
|
|
511
826
|
* @module adapters/serv-adapter-fetch
|
|
512
827
|
*/
|
|
513
828
|
|
|
514
829
|
/**
|
|
515
|
-
*
|
|
830
|
+
* 标准 fetch 签名类型,供 Node 兼容宿主或客户端 in-process 测试注入。签名兼容不代表包整体不依赖 Node API,也不承诺未经验证的边缘部署。
|
|
516
831
|
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
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
|
-
*
|
|
845
|
+
* 将应用转换为标准 fetch 签名,供 Node 兼容宿主或客户端测试注入;签名适配不代表整个包可在无 Node API 的边缘运行时加载。
|
|
524
846
|
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
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
|
-
*
|
|
531
|
-
*
|
|
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
|
-
*
|
|
534
|
-
*
|
|
860
|
+
* const contract = apiContract.create({})
|
|
861
|
+
* const procedures = serv.implement(contract).context<ServContext>().build()
|
|
862
|
+
* const app = serv.createApp({ contract, procedures })
|
|
535
863
|
*
|
|
536
|
-
*
|
|
537
|
-
* const
|
|
538
|
-
* await
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
*
|
|
607
|
-
*
|
|
608
|
-
* serv
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
1045
|
+
* 由真实应用 contract 生成 OpenAPI 3.1 文档。
|
|
660
1046
|
*
|
|
661
|
-
* @param 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
|
-
*
|
|
668
|
-
*
|
|
669
|
-
*
|
|
670
|
-
*
|
|
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 —
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1071
|
+
* 运行时安全策略输入;通常来自 `_core.yml.env` 与 `_serv.yml.cors`。
|
|
741
1072
|
*
|
|
742
1073
|
* @example
|
|
743
1074
|
* ```ts
|
|
744
|
-
* serv
|
|
745
|
-
*
|
|
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
|
-
*
|
|
760
|
-
*
|
|
761
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
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 };
|