@h-ai/serv 0.1.0-alpha.46 → 0.1.0-alpha.48

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
@@ -1,10 +1,10 @@
1
1
  import * as _h_ai_core from '@h-ai/core';
2
2
  import { HaiResult, ValidationFormError } from '@h-ai/core';
3
- import { Router, implement } from '@orpc/server';
4
3
  import { AIFunctions, AudioWsStartMessage, AudioFormat } from '@h-ai/ai';
5
4
  import { AnyContractRouter, OpenAPI } from '@orpc/contract';
6
- import { S as ServSession, a as ServContext, b as ServIam, R as RefreshCookieConfig, C as CreateServContext, p as parseRequestContext, c as buildAuthContextFactory } from './serv-context-BUrtWoFC.js';
7
- export { d as CreateServContextInput, N as NativeRefreshTokenTransportConfig, e as RefreshTokenPair } from './serv-context-BUrtWoFC.js';
5
+ import { ProcedureHandler, ErrorMap, Meta, ProcedureHandlerOptions, ORPCErrorConstructorMap, Router, ContractRouter, Context, ContractProcedure, InferSchemaInput, InferSchemaOutput } from '@orpc/server';
6
+ import { S as ServSession, a as ServContext, b as ServIam, R as RefreshCookieConfig, C as CreateServContext, p as parseRequestContext, c as buildAuthContextFactory } from './serv-context-jc3rZxbj.js';
7
+ export { d as CreateServContextInput, N as NativeRefreshTokenTransportConfig, e as RefreshTokenPair } from './serv-context-jc3rZxbj.js';
8
8
  import { MiddlewareHandler } from 'hono';
9
9
  import { z, ZodType } from 'zod';
10
10
  import { CryptoFunctions, TransportKeyStore } from '@h-ai/crypto';
@@ -55,8 +55,7 @@ interface AudioWsDeps {
55
55
  /**
56
56
  * @h-ai/serv — Pipeline 共享类型
57
57
  *
58
- * 统一定义 Hono middleware 与 oRPC procedure wrapper 的最小公共类型,
59
- * 供默认实现与使用方自定义实现复用。
58
+ * 统一定义 Hono middleware 与 oRPC procedure handler 类型。
60
59
  * @module pipelines/serv-pipeline-types
61
60
  */
62
61
 
@@ -69,17 +68,13 @@ type ServMiddleware = MiddlewareHandler;
69
68
  * - 带配置 middleware:`ServMiddlewareFactory<MyConfig>`
70
69
  */
71
70
  type ServMiddlewareFactory<TConfig = void> = [TConfig] extends [void] ? () => ServMiddleware : (config: TConfig) => ServMiddleware;
72
- /** oRPC procedure handler 的最小上下文约束。 */
73
- interface ServProcedureOptions<TInput = unknown> {
74
- readonly input: TInput;
75
- readonly context: ServContext;
76
- }
77
- /** oRPC procedure handler。 */
78
- type ServProcedureHandler<TInput, TOutput> = (options: ServProcedureOptions<TInput>) => HaiResult<TOutput> | Promise<HaiResult<TOutput>>;
79
- /** 单参数 procedure 包装器(例如 `mapHaiError`、`requireAuth`)。 */
80
- type ServProcedureWrapper = <TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>) => ServProcedureHandler<TInput, TOutput>;
81
- /** 带额外参数的 procedure 包装器(例如 `requirePermission('x', handler)`)。 */
82
- type ServGuardedProcedureWrapper<TRequirement> = <TInput, TOutput>(requirement: TRequirement, handler: ServProcedureHandler<TInput, TOutput>) => ServProcedureHandler<TInput, TOutput>;
71
+ /**
72
+ * 使用 oRPC 公开 `ProcedureHandler` 定义的 HaiResult procedure handler。
73
+ *
74
+ * handler options 完整保留 oRPC 的路径、procedure、signal 与错误构造器信息。
75
+ */
76
+ type ServProcedureOptions<TInput = unknown, TContext extends ServContext = ServContext> = ProcedureHandlerOptions<TContext, TInput, ORPCErrorConstructorMap<ErrorMap>, Meta>;
77
+ type ServProcedureHandler<TInput, TOutput, TContext extends ServContext = ServContext> = ProcedureHandler<TContext, TInput, HaiResult<TOutput>, ErrorMap, Meta>;
83
78
 
84
79
  /**
85
80
  * @h-ai/serv — HTTP 配置
@@ -89,6 +84,13 @@ type ServGuardedProcedureWrapper<TRequirement> = <TInput, TOutput>(requirement:
89
84
  * @module serv-config
90
85
  */
91
86
 
87
+ /** 服务器监听配置(对应 `config/_serv.yml` 的 `server`)。 */
88
+ interface ServServerConfig {
89
+ readonly host: string;
90
+ readonly port: number;
91
+ }
92
+ /** `config/_serv.yml` 中的服务器监听输入类型。 */
93
+ type ServServerConfigInput = Partial<ServServerConfig>;
92
94
  /** OpenAPI JSON endpoint 配置。 */
93
95
  interface ServOpenAPIHttpConfig {
94
96
  readonly path: `/${string}`;
@@ -134,6 +136,7 @@ interface ServHttpConfig {
134
136
  }
135
137
  /** `config/_serv.yml` 的顶层配置结构。 */
136
138
  interface ServConfig {
139
+ readonly server: ServServerConfig;
137
140
  readonly http: ServHttpConfig;
138
141
  readonly cors: ServCorsRuntimeConfig;
139
142
  readonly transport: false | ServTransportRuntimeConfig;
@@ -156,12 +159,17 @@ type ServHttpConfigInput = Partial<{
156
159
  }>;
157
160
  /** `config/_serv.yml` 的顶层输入类型。 */
158
161
  interface ServConfigInput {
162
+ readonly server?: ServServerConfigInput;
159
163
  readonly http?: ServHttpConfigInput;
160
164
  readonly cors?: ServCorsRuntimeConfigInput;
161
165
  readonly transport?: false | ServTransportRuntimeConfigInput;
162
166
  }
163
167
  /** `config/_serv.yml` 对应的配置 Schema。 */
164
168
  declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
169
+ server: z.ZodOptional<z.ZodObject<{
170
+ host: z.ZodDefault<z.ZodString>;
171
+ port: z.ZodDefault<z.ZodNumber>;
172
+ }, z.core.$strip>>;
165
173
  http: z.ZodOptional<z.ZodObject<{
166
174
  apiPrefix: z.ZodDefault<z.ZodCustom<`/api/${string}`, `/api/${string}`>>;
167
175
  openapi: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
@@ -199,10 +207,15 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
199
207
  maxClients: z.ZodDefault<z.ZodNumber>;
200
208
  }, z.core.$strip>]>>;
201
209
  }, z.core.$strip>, z.ZodTransform<{
210
+ server: ServServerConfig;
202
211
  http: ServHttpConfig;
203
212
  cors: ServCorsRuntimeConfig;
204
213
  transport: false | ServTransportRuntimeConfig;
205
214
  }, {
215
+ server?: {
216
+ host: string;
217
+ port: number;
218
+ } | undefined;
206
219
  http?: {
207
220
  apiPrefix: `/api/${string}`;
208
221
  openapi: false | {
@@ -323,7 +336,7 @@ interface ServHttpApp {
323
336
  * **认证 / 会话填充设计**(遵循最小知识原则):
324
337
  *
325
338
  * - **`iam`**(推荐):单一顶层引用。提供后 serv 自动:
326
- * 1. 使用 `iam.session.verifyToken` 填充 `context.session` → `requireAuth/Permission/Role` 生效
339
+ * 1. 使用 `iam.session.verifyToken` 填充 `context.session` → route guard 生效
327
340
  * 2. 若同时启用 `refreshCookie`,使用 `iam.session.refresh` 实现 refresh token 轮换
328
341
  *
329
342
  * - **`refreshCookie`**(可选):浏览器默认用 httpOnly cookie;受信原生客户端可配置 body 通道。与 `iam` 正交:
@@ -534,12 +547,13 @@ declare function toFetch(app: ServHttpApp): ServFetchHandler;
534
547
  interface ServListenOptions {
535
548
  /**
536
549
  * 监听端口。
537
- * 优先级:显式值 > `PORT` 环境变量 > 默认 `3000`。
550
+ * 优先级:显式值 > 默认 `3000`。
551
+ * 环境变量 / 配置文件的 host/port 由 `serv` 配置(`server`)解析后由应用显式传入。
538
552
  */
539
553
  readonly port?: number;
540
554
  /**
541
555
  * 监听地址。
542
- * 优先级:显式值 > `HOST` 环境变量 > 默认 `'127.0.0.1'`(仅本机可达)。
556
+ * 优先级:显式值 > 默认 `'127.0.0.1'`(仅本机可达)。
543
557
  *
544
558
  * - `'127.0.0.1'`:仅本机(推荐用于本地开发、容器内 sidecar、反向代理后端)
545
559
  * - `'0.0.0.0'`:监听所有 IPv4 网卡(容器对外暴露、需公网/内网访问)
@@ -574,7 +588,8 @@ interface ServNodeServer {
574
588
  /**
575
589
  * 在 Node.js 中启动 ServHttpApp。
576
590
  *
577
- * 默认监听 `127.0.0.1:3000`(可通过 `PORT` / `HOST` 环境变量覆盖)。
591
+ * 默认监听 `127.0.0.1:3000`;host/port 建议由应用从 `serv` 配置的 `server`
592
+ *(支持 `HOST` / `PORT` 环境变量覆盖,环境变量高于配置文件)解析后显式传入。
578
593
  * 传入 `onClose` 后自动注册 SIGINT/SIGTERM 优雅关闭,应用层无需手动处理信号。
579
594
  *
580
595
  * @param app - `serv.createApp()` 返回的 HTTP app
@@ -583,111 +598,18 @@ interface ServNodeServer {
583
598
  *
584
599
  * @example
585
600
  * ```ts
586
- * // 最简:读取 PORT / HOST 环境变量,自动优雅关闭
601
+ * // host/port 来自已解析的 serv 配置(env > 配置文件 > 默认)
602
+ * const { server } = core.config.getOrThrow<ServConfig>('serv')
587
603
  * serv.listen(app, {
604
+ * host: server.host,
605
+ * port: server.port,
588
606
  * onListening: info => logger.info('listening', { port: info.port }),
589
607
  * onClose: closeApp,
590
608
  * })
591
- *
592
- * // 容器对外暴露
593
- * serv.listen(app, { host: '0.0.0.0', onClose: closeApp })
594
609
  * ```
595
610
  */
596
611
  declare function listen(app: ServHttpApp, options?: ServListenOptions): ServNodeServer;
597
612
 
598
- /**
599
- * @h-ai/serv — Pipeline 公共 helper
600
- *
601
- * 放置不属于 Hono middleware / oRPC wrapper 默认实现本身、
602
- * 但会被多个 pipeline 复用的公共工具。
603
- * @module pipelines/serv-pipeline-helper
604
- */
605
-
606
- /**
607
- * 捕获未处理异常并转换为 HaiResult。
608
- *
609
- * 虽然它不是 middleware / 鉴权 wrapper,但属于所有 procedure pipeline
610
- * 都可能复用的“公共包装能力”,因此与 `buildHaiErrorBody()` 一起放在 helper 中。
611
- *
612
- * @param handler - 被包装的 procedure handler
613
- * @returns 带异常保护的新 handler
614
- *
615
- * @example
616
- * ```ts
617
- * const safeHandler = serv.mapHaiError(myHandler)
618
- * ```
619
- */
620
- declare function mapHaiError<TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
621
-
622
- /**
623
- * @h-ai/serv — requireAuth 默认实现
624
- * @module pipelines/serv-pipeline-require-auth
625
- */
626
-
627
- /**
628
- * 需要已验证会话的 procedure 包装器。
629
- *
630
- * `context.session` 由 `buildAuthContextFactory(verifyToken)`(或 `serv.createApp({ iam })` 自动启用)填充。
631
- * token 不存在或无效(verifyToken 失败)时 `session` 均为 `undefined`,统一返回 401 UNAUTHORIZED。
632
- * 内置调用 `mapHaiError`,无需外层再次封装。
633
- *
634
- * @param handler - 被包装的 procedure handler
635
- * @returns 带认证检查的新 handler
636
- *
637
- * @example
638
- * ```ts
639
- * const protectedHandler = serv.requireAuth(myHandler)
640
- * ```
641
- */
642
- declare function requireAuth<TInput, TOutput>(handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
643
-
644
- /**
645
- * @h-ai/serv — requirePermission 默认实现
646
- * @module pipelines/serv-pipeline-require-permission
647
- */
648
-
649
- /**
650
- * 需要指定权限的 procedure 包装器。
651
- *
652
- * 内置调用 `requireAuth`,在验证 Token 后进一步匹配 `session.permissions`。
653
- * 拥有 {@link WILDCARD_PERMISSION}(`'*'`)的用户自动通过所有权限检查。
654
- *
655
- * @param permission - 所需权限字符串(如 `'iam.users.read'`)
656
- * @param handler - 被包装的 procedure handler
657
- * @returns 带权限检查的新 handler
658
- *
659
- * @example
660
- * ```ts
661
- * const adminHandler = serv.requirePermission('iam.users.write', myHandler)
662
- * ```
663
- */
664
- declare function requirePermission<TInput, TOutput>(permission: string, handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
665
-
666
- /**
667
- * @h-ai/serv — requireRole 默认实现
668
- * @module pipelines/serv-pipeline-require-role
669
- */
670
-
671
- /**
672
- * 需要指定角色的 procedure 包装器。
673
- *
674
- * 内置调用 `requireAuth`,在验证 Token 后进一步匹配 `session.roles`。
675
- * 拥有 {@link WILDCARD_ROLE}(`'*'`)的用户自动通过所有角色检查。
676
- *
677
- * **使用建议**:优先使用 {@link requirePermission}(基于行为的细粒度授权);
678
- * `requireRole` 适用于按角色分流的粗粒度路由(如 `'admin'` 专属入口)。
679
- *
680
- * @param role - 所需角色字符串(如 `'admin'`)
681
- * @param handler - 被包装的 procedure handler
682
- * @returns 带角色检查的新 handler
683
- *
684
- * @example
685
- * ```ts
686
- * const adminOnly = serv.requireRole('admin', myHandler)
687
- * ```
688
- */
689
- declare function requireRole<TInput, TOutput>(role: string, handler: ServProcedureHandler<TInput, TOutput>): ServProcedureHandler<TInput, TOutput>;
690
-
691
613
  /**
692
614
  * @h-ai/serv — CORS middleware
693
615
  *
@@ -746,6 +668,86 @@ interface GenerateOpenAPISpecOptions {
746
668
  */
747
669
  declare function generateSpec(contract: AnyContractRouter, options?: GenerateOpenAPISpecOptions): Promise<OpenAPI.Document>;
748
670
 
671
+ /**
672
+ * @h-ai/serv — contract-first 链式 procedure 实现器
673
+ *
674
+ * 在 oRPC `implement()` 之上提供 route 名只出现一次的链式 DSL。
675
+ * 应用只接触 contract 路径、上下文与 guard,不需要组装 oRPC router 对象。
676
+ *
677
+ * @module serv-router
678
+ */
679
+
680
+ type EmptyContext = Record<never, never>;
681
+ type StringKey<T> = Extract<keyof T, string>;
682
+ type ServContract = ContractRouter<Meta>;
683
+ /** contract 中所有 procedure 的点分路径。 */
684
+ type ServRoutePath<TContract extends ServContract> = TContract extends ContractProcedure<infer _TInputSchema, infer _TOutputSchema, infer _TErrorMap, infer _TMeta> ? never : {
685
+ [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;
686
+ }[StringKey<TContract>];
687
+ 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;
688
+ type RouteContract<TContract extends ServContract, TPath extends ServRoutePath<TContract>> = ContractAtPath<TContract, TPath>;
689
+ /** `.auth()`、`.permission()`、`.role()` 后的非空会话上下文。 */
690
+ type AuthenticatedServContext<TContext extends ServContext = ServContext> = TContext & {
691
+ readonly session: NonNullable<TContext['session']>;
692
+ };
693
+ 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;
694
+ /** 从 contract procedure 精确推导当前 route 的 HaiResult handler。 */
695
+ type ServRouteHandler<TContract extends ServContract, TPath extends ServRoutePath<TContract>, TContext extends Context> = RouteHandler<RouteContract<TContract, TPath>, TContext>;
696
+ type ServGuardedRouteHandler<TContract extends ServContract, TPath extends ServRoutePath<TContract>, TContext extends ServContext> = RouteHandler<RouteContract<TContract, TPath>, AuthenticatedServContext<TContext>>;
697
+ 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>>;
698
+ /** 尚未声明 guard 的单 route 注册器。 */
699
+ interface ServRouteRegistration<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract>, TPath extends TRemaining> {
700
+ readonly auth: () => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
701
+ readonly permission: (permission: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
702
+ readonly role: (role: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
703
+ }
704
+ /** 已声明认证或授权 guard 的单 route 注册器。 */
705
+ interface ServGuardedRouteRegistration<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract>, TPath extends TRemaining> {
706
+ readonly auth: () => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
707
+ readonly permission: (permission: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
708
+ readonly role: (role: string) => ServGuardedRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
709
+ readonly handle: (handler: ServGuardedRouteHandler<TContract, TPath, TCurrentContext>) => NextBuilder<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
710
+ }
711
+ interface ServRouteMethod<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract>> {
712
+ <TPath extends TRemaining>(path: TPath, handler: ServRouteHandler<TContract, TPath, TCurrentContext>): NextBuilder<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
713
+ <TPath extends TRemaining>(path: TPath): ServRouteRegistration<TContract, TInitialContext, TCurrentContext, TRemaining, TPath>;
714
+ }
715
+ type ServRouterBuilderBuild<TContract extends ServContract, TInitialContext extends Context, TRemaining> = [TRemaining] extends [never] ? {
716
+ /** 所有 contract procedures 均实现后才可调用。 */
717
+ readonly build: () => Router<TContract, TInitialContext>;
718
+ } : {
719
+ /** 仅供 TypeScript/IDE 展示尚未实现的 route;运行时不存在。 */
720
+ readonly missingRoutes: TRemaining;
721
+ };
722
+ /**
723
+ * 链式 router builder。
724
+ *
725
+ * `TRemaining` 会随着 `.route()` 注册逐步收窄;未实现完整 contract 时没有 `.build()`。
726
+ */
727
+ type ServRouterBuilder<TContract extends ServContract, TInitialContext extends Context, TCurrentContext extends ServContext, TRemaining extends ServRoutePath<TContract> = ServRoutePath<TContract>> = {
728
+ readonly route: ServRouteMethod<TContract, TInitialContext, TCurrentContext, TRemaining>;
729
+ } & ServRouterBuilderBuild<TContract, TInitialContext, TRemaining>;
730
+ /** hai-framework 链式 contract 实现器。 */
731
+ interface ServImplementer<TContract extends ServContract> {
732
+ readonly context: <TContext extends ServContext>() => ServRouterBuilder<TContract, TContext & EmptyContext, TContext>;
733
+ }
734
+ /**
735
+ * 创建链式 contract 实现器。
736
+ *
737
+ * @example
738
+ * ```ts
739
+ * serv
740
+ * .implement(contract)
741
+ * .context<ServContext>()
742
+ * .route('info', infoHandler)
743
+ * .route('echo')
744
+ * .auth()
745
+ * .handle(echoHandler)
746
+ * .build()
747
+ * ```
748
+ */
749
+ declare function implement<TContract extends ServContract>(contract: TContract): ServImplementer<TContract>;
750
+
749
751
  /**
750
752
  * @h-ai/serv — 部署环境 HTTP 安全策略
751
753
  *
@@ -877,12 +879,15 @@ declare function resolveRequestLocale(headers: Headers | Record<string, string |
877
879
  *
878
880
  * const Schema = z.object({ name: z.string().min(1) })
879
881
  *
880
- * export const handler = serv.mapHaiError(async ({ input, context }) => {
881
- * const result = serv.validateInputOrFail(Schema, input, context.locale)
882
- * if (!result.success) return result
883
- * const { name } = result.data
884
- * // ...
885
- * })
882
+ * export const procedures = serv
883
+ * .implement(contract)
884
+ * .context<ServContext>()
885
+ * .route('profile.update', ({ input, context }) => {
886
+ * const result = serv.validateInputOrFail(Schema, input, context.locale)
887
+ * if (!result.success) return result
888
+ * return profileService.update(result.data)
889
+ * })
890
+ * .build()
886
891
  * ```
887
892
  *
888
893
  * @param schema - Zod schema
@@ -903,16 +908,11 @@ declare const serv: {
903
908
  storageAssets: typeof storageAssets;
904
909
  createRuntimeSecurityPolicy: typeof createRuntimeSecurityPolicy;
905
910
  generateSpec: typeof generateSpec;
906
- /** 基于 contract 创建可注入 context 的 procedure builder(封装 `@orpc/server` 的 `implement`)。 */
907
911
  implement: typeof implement;
908
- requireAuth: typeof requireAuth;
909
- requirePermission: typeof requirePermission;
910
- requireRole: typeof requireRole;
911
- mapHaiError: typeof mapHaiError;
912
912
  validateInputOrFail: typeof validateInputOrFail;
913
913
  WILDCARD_PERMISSION: string;
914
914
  WILDCARD_ROLE: string;
915
915
  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;
916
916
  };
917
917
 
918
- 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 ServGuardedProcedureWrapper, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, type ServProcedureWrapper, type ServRpcHttpConfig, type ServRuntimeSecurityPolicy, type ServRuntimeSecurityPolicyInput, ServSession, type ServStorageAssetsConfig, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };
918
+ 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, 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 };
package/dist/index.js CHANGED
@@ -1,7 +1,5 @@
1
- import { WILDCARD_PERMISSION, requirePermission } from './chunk-YQV2OMQK.js';
2
- import { servM, mapHaiError, requireAuth, normalizeServLocale, buildHaiErrorBody } from './chunk-4QBF4PUG.js';
3
- import { implement } from '@orpc/server';
4
- import process2 from 'process';
1
+ import { servM, WILDCARD_ROLE, WILDCARD_PERMISSION, implement, normalizeServLocale, buildHaiErrorBody } from './chunk-KIWHKWLD.js';
2
+ import process from 'process';
5
3
  import { serve } from '@hono/node-server';
6
4
  import { apiContract, AUDIO_WS_PATH, AudioWsClientMessageSchema } from '@h-ai/api-contract';
7
5
  import { core, ok, err, HaiCommonError } from '@h-ai/core';
@@ -662,6 +660,10 @@ var TransportRuntimeConfigInputSchema = z.union([
662
660
  maxClients: z.number().int().positive().default(1e4)
663
661
  })
664
662
  ]);
663
+ var ServerConfigInputSchema = z.object({
664
+ host: z.string().min(1).default("127.0.0.1"),
665
+ port: z.number().int().min(1).max(65535).default(3e3)
666
+ });
665
667
  var CorsRuntimeConfigInputSchema = z.object({
666
668
  origin: z.string().optional(),
667
669
  nativeOrigins: z.string().optional(),
@@ -677,14 +679,32 @@ var ServHttpConfigInputSchema = z.object({
677
679
  rpc: RpcHttpConfigInputSchema.default(false)
678
680
  });
679
681
  var ServConfigSchema = z.object({
682
+ server: ServerConfigInputSchema.optional(),
680
683
  http: ServHttpConfigInputSchema.optional(),
681
684
  cors: CorsRuntimeConfigInputSchema.optional(),
682
685
  transport: TransportRuntimeConfigInputSchema.optional()
683
- }).transform(({ http, cors: cors2, transport }) => ({
686
+ }).transform(({ server, http, cors: cors2, transport }) => ({
687
+ server: resolveServServerConfig(server ?? {}),
684
688
  http: resolveServHttpConfig(http ?? {}),
685
689
  cors: resolveServCorsRuntimeConfig(cors2 ?? {}),
686
690
  transport: resolveServTransportRuntimeConfig(transport ?? false)
687
691
  }));
692
+ function readServerEnvOverride() {
693
+ const override = {};
694
+ const host = process.env.HOST;
695
+ if (host)
696
+ override.host = host;
697
+ const rawPort = process.env.PORT;
698
+ if (rawPort) {
699
+ const port = Number(rawPort);
700
+ if (Number.isInteger(port) && port >= 1 && port <= 65535)
701
+ override.port = port;
702
+ }
703
+ return override;
704
+ }
705
+ function resolveServServerConfig(input = {}) {
706
+ return { ...ServerConfigInputSchema.parse(input), ...readServerEnvOverride() };
707
+ }
688
708
  function resolveServHttpConfig(input = {}) {
689
709
  return ServHttpConfigInputSchema.parse(input);
690
710
  }
@@ -757,7 +777,7 @@ function resolveRefreshCookieConfig(apiPrefix, config, iam) {
757
777
  return {
758
778
  cookieName: config.cookieName ?? "hai_refresh_token",
759
779
  maxAge: config.maxAge ?? 30 * 24 * 3600,
760
- isSecure: config.secure ?? process2.env.NODE_ENV === "production",
780
+ isSecure: config.secure ?? process.env.NODE_ENV === "production",
761
781
  refreshCookiePath: `${apiPrefix}${IAM_AUTH_PATHS.refresh}`,
762
782
  onRefresh,
763
783
  nativeTokenTransport: config.nativeTokenTransport
@@ -1288,8 +1308,8 @@ function isOrpcValidationErrorPayload(value) {
1288
1308
  var DEFAULT_SERV_HOST = "127.0.0.1";
1289
1309
  var DEFAULT_SERV_PORT = 3e3;
1290
1310
  function listen(app, options = {}) {
1291
- const host = options.host ?? process2.env.HOST ?? DEFAULT_SERV_HOST;
1292
- const port = options.port ?? (process2.env.PORT ? Number(process2.env.PORT) : DEFAULT_SERV_PORT);
1311
+ const host = options.host ?? DEFAULT_SERV_HOST;
1312
+ const port = options.port ?? DEFAULT_SERV_PORT;
1293
1313
  const server = serve(
1294
1314
  { fetch: app.fetch, hostname: host, port },
1295
1315
  options.onListening
@@ -1324,20 +1344,11 @@ function listen(app, options = {}) {
1324
1344
  } catch {
1325
1345
  }
1326
1346
  };
1327
- process2.once("SIGINT", () => void shutdown());
1328
- process2.once("SIGTERM", () => void shutdown());
1347
+ process.once("SIGINT", () => void shutdown());
1348
+ process.once("SIGTERM", () => void shutdown());
1329
1349
  }
1330
1350
  return handle;
1331
1351
  }
1332
- var WILDCARD_ROLE = "*";
1333
- function requireRole(role, handler) {
1334
- return requireAuth(async (options) => {
1335
- const roles = options.context.session?.roles ?? [];
1336
- if (!roles.includes(role) && !roles.includes(WILDCARD_ROLE))
1337
- return err(HaiCommonError.FORBIDDEN, servM("serv_errorForbidden", { locale: options.context.locale }));
1338
- return handler(options);
1339
- });
1340
- }
1341
1352
 
1342
1353
  // src/serv-cors.ts
1343
1354
  var DEFAULT_CORS_METHODS = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE", "OPTIONS"];
@@ -1569,14 +1580,9 @@ var serv = {
1569
1580
  createRuntimeSecurityPolicy,
1570
1581
  // OpenAPI / 导出
1571
1582
  generateSpec,
1572
- // Procedure 装配(contract 路由定义统一使用 `apiContract.route`)。
1573
- /** 基于 contract 创建可注入 context 的 procedure builder(封装 `@orpc/server` 的 `implement`)。 */
1583
+ // Contract procedure 装配。
1574
1584
  implement,
1575
- // oRPC procedure 包装器
1576
- requireAuth,
1577
- requirePermission,
1578
- requireRole,
1579
- mapHaiError,
1585
+ // Procedure 工具;认证与授权统一由 route 链声明。
1580
1586
  validateInputOrFail,
1581
1587
  // 通配符常量
1582
1588
  WILDCARD_PERMISSION,