@h-ai/kit 1.0.0-beta.19 → 1.0.0-beta.20

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.md CHANGED
@@ -4,8 +4,6 @@
4
4
 
5
5
  [框架首页](../../README.md) · 相关模块:[@h-ai/core](../core/README.md) · [@h-ai/crypto](../crypto/README.md) · [@h-ai/iam](../iam/README.md)
6
6
 
7
- ## 简介
8
-
9
7
  `@h-ai/kit` 是 hai-framework 的 SvelteKit 集成模块,提供统一的请求管道与同源 endpoint 工具:
10
8
 
11
9
  运行时统一通过 `kit.xx` 使用。根入口公开 `KitFunctions`(服务端)和 `BrowserKitFunctions`(浏览器)命名空间类型,浏览器条件入口的 `kit` 仅包含客户端、CRUD、页面 Token 与默认语言操作。`AuthCookieWriter` 是服务端认证对当前请求 Cookie 的最小写入契约。导出这些方法或命名空间时,可直接生成消费者声明,无需导入内部 helper。
@@ -16,19 +14,7 @@
16
14
 
17
15
  > 公共跨端 HTTP API contract 统一放在 `@h-ai/api-contract`,由 `@h-ai/serv` 挂载,客户端通过 `@h-ai/api-client` typed client 调用;`@h-ai/kit` 不定义也不承载业务 API contract。
18
16
 
19
- ## 核心功能清单
20
-
21
- - Handle Hook 组合(`createHandle` / `sequence`)
22
- - 认证/角色/权限守卫(`guard.require` / `guard.check`)
23
- - 内置 Logging / RateLimit / CORS 中间件(通过 `createHandle` 配置启用)
24
- - 统一 API 响应与请求验证(Zod)
25
- - Bearer Token 工具(服务端 login/logout + 浏览器端 Token 存储)
26
- - 浏览器端统一客户端(自动 CSRF,支持传输加密)
27
- - 声明式 CRUD 资源定义(`crud.define`)
28
- - A2A 协议集成:默认发现路径 `/.well-known/agent-card.json`;配置 `authenticate` 后 null/undefined 返回 401,仅未配置认证时允许匿名。API Key 参数从完整 Agent Card 的 `securitySchemes.apiKey` 读取。Hook 与显式 A2A 路由共享 JSON-RPC 解析错误和 SSE 取消清理逻辑。
29
- - 双构建模式(`createAdapter()`)
30
-
31
- ## 使用入门
17
+ ## 快速开始
32
18
 
33
19
  `sequence` 保留并组合 SvelteKit 的 `resolve` 选项:页面转换由内向外执行,`preload` 与序列化响应头过滤使用首个配置,确保 i18n 的 HTML 语言标记正确替换。
34
20
 
@@ -40,14 +26,7 @@ npm install svelte @sveltejs/kit zod
40
26
  npm install -D @sveltejs/vite-plugin-svelte vite
41
27
  ```
42
28
 
43
- ### 依赖
44
-
45
- - `@h-ai/iam` — 身份认证与授权(A2A 认证),可选;使用 A2A 功能时需在 kit 使用前初始化
46
- - `@h-ai/crypto` — 加密(传输加密 / Cookie 加密),可选;启用加密时需在 kit 使用前初始化
47
-
48
- ### 快速开始
49
-
50
- #### 项目配置
29
+ ### 项目配置
51
30
 
52
31
  **vite.config.ts**:
53
32
 
@@ -83,7 +62,72 @@ export default defineConfig({
83
62
 
84
63
  使用 Crypto 或依赖它的 Kit/IAM 时,应用生产依赖需包含 `sm-crypto@0.5.8`。CLI 已配好这一项;手工接入时运行 `pnpm add sm-crypto@0.5.8`。仅设置 external 不够,adapter-node 还需要这项直接依赖声明才能保留原包加载。
85
64
 
86
- #### 搭建请求管道
65
+ ## 功能
66
+
67
+ ### 用途、输入与输出
68
+
69
+ 下表的调用片段复用本页已初始化的服务;命名变量代表应用已提供的请求、Schema、资源定义或配置。完整步骤和失败处理见对应功能示例。`HaiResult` 先判断 `success`,异步项按签名 `await`。
70
+
71
+ | 入口 | 场景 | 输入 | 输出 | 调用片段 |
72
+ | ------------------------------------------- | ------------------ | ------------------------------------------------------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------- |
73
+ | `kit.createHandle` | 请求管道 | `HandleConfig: auth/guards/logging/rateLimit/crypto/access` | `SvelteKit Handle` | `kit.createHandle({ logging: true })` |
74
+ | `kit.sequence` | 组合 Hook | `...Handle` | `Handle preserving resolve options` | `kit.sequence(i18nHandle, authHandle)` |
75
+ | `kit.handler` | 端点异常边界 | `(RequestEvent) => Response \| Promise<Response>` | `SvelteKit request handler` | `kit.handler(async () => kit.response.ok({ ready: true }))` |
76
+ | `kit.guard.check` | 检查权限 | `session, permission` | `boolean` | `kit.guard.check(session, "orders.read")` |
77
+ | `kit.guard.require` | 要求权限 | `session, permission` | `void; denied access throws Response` | `kit.guard.require(session, "orders.write")` |
78
+ | `kit.guard.requireSensitive` | 确认操作用途 | `initialized IAM, current RequestEvent request/cookies, operation` | `Promise<void>; invalid proof throws Response` | `kit.guard.requireSensitive(iam, event, "orders.approve")` |
79
+ | `kit.response.ok` | 成功响应 | `data, requestId?` | `Response with HTTP 200 and success data` | `kit.response.ok({ id: "o1" })` |
80
+ | `kit.response.created` | 创建响应 | `data, requestId?` | `Response with HTTP 201 and success data` | `kit.response.created({ id: "o1" })` |
81
+ | `kit.response.noContent` | 无正文 | `none` | `Response with HTTP 204` | `kit.response.noContent()` |
82
+ | `kit.response.error` | 指定失败 | `code, message, status?, requestId?, public details?` | `safe failure Response` | `kit.response.error("app:orders:001", "请求无效", 400)` |
83
+ | `kit.response.badRequest` | 无效请求 | `message?, requestId?` | `HTTP 400 Response` | `kit.response.badRequest("Invalid request")` |
84
+ | `kit.response.unauthorized` | 未认证 | `message?, requestId?` | `HTTP 401 Response` | `kit.response.unauthorized()` |
85
+ | `kit.response.forbidden` | 无权限 | `message?, requestId?` | `HTTP 403 Response` | `kit.response.forbidden()` |
86
+ | `kit.response.notFound` | 未找到 | `message?, requestId?` | `HTTP 404 Response` | `kit.response.notFound()` |
87
+ | `kit.response.conflict` | 版本冲突 | `message, requestId?` | `HTTP 409 Response` | `kit.response.conflict("Data changed")` |
88
+ | `kit.response.validationError` | 字段校验 | `{ field, message }[], requestId?` | `HTTP 422 Response; errors in safe error.cause` | `kit.response.validationError([{ field: "title", message: "请输入标题" }])` |
89
+ | `kit.response.internalError` | 内部失败 | `message?, requestId?` | `HTTP 500 Response with safe message` | `kit.response.internalError()` |
90
+ | `kit.response.redirect` | 重定向 | `location, status?` | `redirect Response` | `kit.response.redirect("/auth/login")` |
91
+ | `kit.response.fromResult` | 映射领域结果 | `HaiResult<T>, httpStatusMap?, requestId?` | `HTTP Response; preserves safe code/status/traceId` | `kit.response.fromResult(result)` |
92
+ | `kit.response.fromError` | 映射领域错误 | `HaiError, requestId?` | `safe HTTP failure Response` | `kit.response.fromError(result.error)` |
93
+ | `kit.validate.body` | JSON/表单校验 | `Request, Zod schema` | `Promise<schema data>; invalid input throws Response` | `kit.validate.body(request, InputSchema)` |
94
+ | `kit.validate.query` | 查询校验 | `URL, Zod schema` | `schema data; invalid input throws Response` | `kit.validate.query(url, QuerySchema)` |
95
+ | `kit.validate.params` | 路径参数校验 | `Record<string,string>, Zod schema` | `schema data; invalid input throws Response` | `kit.validate.params(params, IdSchema)` |
96
+ | `kit.validate.IdParamSchema` | 通用资源编号 | `{ id: string }` | `Zod parsed ID object` | `kit.validate.IdParamSchema.parse({ id: "o1" })` |
97
+ | `kit.validate.PaginationQuerySchema` | 分页输入 | `page/pageSize/search query` | `normalized pagination object` | `kit.validate.PaginationQuerySchema.parse({ page: "1" })` |
98
+ | `kit.client.create` | 同源客户端 | `KitClientConfig: auth/transport/recoverSession?` | `KitClient { apiFetch, init, destroy, ready }` | `kit.client.create()` |
99
+ | `kit.client.createAuthActions` | 标准认证动作 | `{ basePath?, ...auth client options }?` | `AuthPanel-compatible actions` | `kit.client.createAuthActions({ basePath: "/api/auth" })` |
100
+ | `kit.client.installBrowserTransport` | 按公共配置安装加密 | `public KitConfig, { crypto, basePath? }` | `void; installs same-origin global fetch wrapper when enabled` | `kit.client.installBrowserTransport(publicKitConfig, { crypto })` |
101
+ | `kit.client.installBrowserTransportFetch` | 自定义传输安装 | `ClientTransportConfig` | `void; installs browser global fetch wrapper` | `kit.client.installBrowserTransportFetch(transportOptions)` |
102
+ | `kit.auth.publicPaths` | 匿名端点名单 | `none` | `string[]; prepend the host base path when needed` | `kit.auth.publicPaths` |
103
+ | `kit.auth.createApi` | 认证 handler 集合 | `initialized IAM service` | `RequestHandler map for login/OTP/reset/config/etc.` | `kit.auth.createApi(iam)` |
104
+ | `kit.auth.login` | 密码登录 | `Cookie writer, PasswordCredentials, request context?` | `Promise<HaiResult<AuthResult>>; writes token cookies on success` | `kit.auth.login(cookies, { identifier: "alice", password })` |
105
+ | `kit.auth.loginWithOtp` | 验证码登录 | `Cookie writer, OtpCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithOtp(cookies, otpCredentials)` |
106
+ | `kit.auth.loginWithLdap` | LDAP 登录 | `Cookie writer, LdapCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithLdap(cookies, ldapCredentials)` |
107
+ | `kit.auth.loginWithApiKey` | API Key 登录 | `Cookie writer, ApiKeyCredentials, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.loginWithApiKey(cookies, apiKeyCredentials)` |
108
+ | `kit.auth.registerAndLogin` | 注册并登录 | `Cookie writer, RegisterOptions, request context?` | `Promise<HaiResult<AuthResult>>` | `kit.auth.registerAndLogin(cookies, registration)` |
109
+ | `kit.auth.logout` | 显式退出 | `Cookie writer, accessToken?` | `Promise<HaiResult<void>>` | `kit.auth.logout(cookies, accessToken)` |
110
+ | `kit.auth.setBrowserToken` | 页面内存凭据 | `access token string` | `void; no Web Storage persistence` | `kit.auth.setBrowserToken(accessToken)` |
111
+ | `kit.auth.clearBrowserToken` | 清理页面凭据 | `none` | `void` | `kit.auth.clearBrowserToken()` |
112
+ | `kit.crud.define` | 声明 CRUD 资源 | `CrudResourceConfig` | `resource operation object` | `kit.crud.define(resourceDefinition)` |
113
+ | `kit.middleware.rateLimit` | 固定窗口限流 | `{ windowMs, maxRequests, store, ...options }` | `Middleware; 429 on quota and 503 on store failure` | `kit.middleware.rateLimit({ windowMs: 60000, maxRequests: 100, store })` |
114
+ | `kit.middleware.createCacheRateLimitStore` | 共享计数器 | `initialized shared cache, { namespace, allowMemory? }` | `RateLimitStore` | `kit.middleware.createCacheRateLimitStore(cache, { namespace: "orders" })` |
115
+ | `kit.middleware.createMemoryRateLimitStore` | 开发计数器 | `none; development or explicit test only` | `MemoryRateLimitStore` | `kit.middleware.createMemoryRateLimitStore()` |
116
+ | `kit.createFeedbackHandler` | 受控反馈入口 | `{ feedback, resolveContext, ...options }` | `catch-all RequestHandler` | `kit.createFeedbackHandler({ feedback, resolveContext })` |
117
+ | `kit.i18n.setLocale` | 启动默认语言 | `locale string` | `void; do not change process default per request` | `kit.i18n.setLocale("zh-CN")` |
118
+ | `kit.i18n.runWithLocale` | 请求语言隔离 | `locale, callback; Node server only` | `same callback return value` | `kit.i18n.runWithLocale("en-US", () => resolve(event))` |
119
+
120
+ - Handle Hook 组合(`createHandle` / `sequence`)
121
+ - 认证/角色/权限守卫(`guard.require` / `guard.check`)
122
+ - 内置 Logging / RateLimit / CORS 中间件(通过 `createHandle` 配置启用)
123
+ - 统一 API 响应与请求验证(Zod)
124
+ - Bearer Token 工具(服务端 login/logout + 浏览器端 Token 存储)
125
+ - 浏览器端统一客户端(自动 CSRF,支持传输加密)
126
+ - 声明式 CRUD 资源定义(`crud.define`)
127
+ - A2A 协议集成:默认发现路径 `/.well-known/agent-card.json`;配置 `authenticate` 后 null/undefined 返回 401,仅未配置认证时允许匿名。API Key 参数从完整 Agent Card 的 `securitySchemes.apiKey` 读取。Hook 与显式 A2A 路由共享 JSON-RPC 解析错误和 SSE 取消清理逻辑。
128
+ - 双构建模式(`createAdapter()`)
129
+
130
+ ### 搭建请求管道
87
131
 
88
132
  应用部署在 `/yyy/` 等子目录时,SvelteKit 的 `paths.base` 与守卫使用的实际 URL 应保持一致:将 `resolve('/').replace(/\/$/, '')`(配置 `paths.relative: false`) 加到 `protectedPaths`、`publicPaths` 和 `loginUrl`,例如 `${base}/api/*`。认证页面使用 ``kit.client.createAuthActions({ basePath: `${base}/api/auth` })``。Kit 根据应用路由识别 `/api/*`,包括路由分组和挂载前缀;未认证的 API 返回 401,页面按配置跳转登录。生成模板已处理这些路径。
89
133
 
@@ -92,7 +136,7 @@ export default defineConfig({
92
136
  **src/hooks.server.ts**:
93
137
 
94
138
  ```typescript
95
- import type { Handle } from '@sveltejs/kit'
139
+ import type { Handle } from '@sveltejs/kit/hooks'
96
140
  import { cache } from '@h-ai/cache'
97
141
  import { iam } from '@h-ai/iam'
98
142
  import { kit } from '@h-ai/kit'
@@ -120,7 +164,7 @@ export const handle: Handle = haiHandle
120
164
 
121
165
  限流采用首次成功提交为起点的固定窗口,后续请求不延长 TTL。必须显式传 store;应用先检查共享 Redis 初始化结果,所有节点使用相同 namespace/windowMs/maxRequests。缓存读取/条件提交失败或冲突耗尽返回503,超限429;不会回退 get+set 或本地计数。createMemoryRateLimitStore 仅开发或显式 HAI_E2E/HAI_E2E_MOCK 测试使用,无常驻定时器,丢弃引用即可回收;每次请求最多惰性扫描 64 项,最多保存 10000 个窗口,容量满时对新 key 返回503且不驱逐有效计数,外部 store 生命周期由创建者管理;生产不得设置测试开关。响应重置时间为节点时钟估算,实际窗口由 Redis TTL 决定,须同步时钟。默认键只读取可信 adapter 的 getClientAddress、规范化等价IPv6和IPv4映射地址,不采信原始转发头;代理信任通过部署adapter配置。
122
166
 
123
- #### API 端点
167
+ ### API 端点
124
168
 
125
169
  ```typescript
126
170
  import { kit } from '@h-ai/kit'
@@ -138,7 +182,7 @@ export const POST = kit.handler(async ({ request, locals }) => {
138
182
  })
139
183
  ```
140
184
 
141
- #### 浏览器客户端
185
+ ### 浏览器客户端
142
186
 
143
187
  `kit.client.create()` 面向 SvelteKit 同源 endpoint;Web / App / 小程序跨域访问公共 API 时,请使用 `@h-ai/api-client`。
144
188
 
@@ -162,7 +206,7 @@ const response = await apiFetch('/api/users', { method: 'GET' })
162
206
  >
163
207
  > 同源 SvelteKit endpoint 使用 httpOnly Cookie,由服务端读取 Cookie 完成鉴权。浏览器 Bearer Token 使用 `kit.auth.setBrowserToken()` 写入页面内存,再通过 `kit.client.create({ auth: true })` 注入。自定义 `BrowserTokenStore` 也必须使用内存或平台安全存储,禁止将敏感凭据写入 Web Storage。
164
208
 
165
- #### 同源传输加密
209
+ ### 同源传输加密
166
210
 
167
211
  kit 的传输加密也统一委托 `@h-ai/crypto`:服务端通过 `crypto.transport.createServer({ serverKeyRing, keyStore })` 创建管理器,浏览器端通过 `kit.client.installBrowserTransport(kitConfig, { crypto })` 一行安装同源 fetch 包装。服务端还须注入受管密钥环和共享 registry。
168
212
 
@@ -248,9 +292,7 @@ export const { apiFetch } = kit.client.create()
248
292
 
249
293
  transport 默认作用于同源 `/api/*` endpoint 与 SvelteKit `__data.json` 页面数据请求(以及密钥协商端点本身);页面文档与静态资源仍保持明文,不要求携带 `X-Client-Id`。文件上传等 `multipart/form-data` 请求会保持原样发送,需在服务端 `excludePaths` 中显式放行对应上传路径。
250
294
 
251
- 认证协议仅支持 v2:独立随机 SM4-CBC 密钥和 HMAC-SM3 密钥由 SM2 封装,先验签、再验证 MAC,最后解密 CBC。请求由客户端私钥签名,响应由所选服务端版本签名。认证字段包括 version、direction、serverKeyId、clientId、128 位随机 requestId(兼 nonce)、issuedAt、HTTP method、pathname + 原始 search 的 SM3 pathHash,以及响应 status。请求默认有效期为 5 分钟,客户端时间最多可领先服务器 30 秒。服务端可用 `maxRequestAgeSeconds`(1~600)和 `maxFutureSkewSeconds`(0~300)调整,单位为整数秒。共享 registry 原子消费 (clientId, requestId),nonce 保留到 issuedAt + 11 分钟,覆盖最长可配窗口及节点时钟差,存储故障拒绝请求。Redis 必须保留有效 nonce,使用 noeviction/可靠持久化策略;各节点时钟须同步。此能力防止同一传输消息重放,业务重试仍需自己的幂等键。
252
-
253
- 空 POST 也发送空明文的认证封装;GET/HEAD 将封装放在 X-Hai-Transport 头,204/304/HEAD 等无正文响应也使用同名头承载认证封装。其它请求/响应在 JSON 正文承载封装并带 X-Encrypted: true。客户端必须关联原请求上下文和 HTTP status,缺标记、旧格式或认证失败一律 reject;明文接口由 Serv/Kit 路径规则 `encryption: disabled` 或宿主 `excludePaths` 显式允许;调用方同步选择普通请求,API Client/Kit 包装器使用自己的 `excludePaths`。CORS 须允许并暴露 X-Hai-Transport。握手公钥的真实性依赖 TLS,不替代 IAM 业务授权。
295
+ 传输使用认证 v2 封装,先验签和 MAC 再解密,并绑定方法、路径、时间与响应状态。共享 registry 原子消费 nonce;TLS、可信代理、业务授权及幂等仍由宿主负责。自定义跨域入口须允许并暴露 `X-Hai-Transport`。完整字段、无正文响应、时钟窗口和防重放要求统一见 [Transport 协议](../crypto/TRANSPORT-PROTOCOL.md)。
254
296
 
255
297
  业务容量通过 `transport.maxPlaintextBytes` 统一配置:默认 1 MiB,整数范围 1 KiB~64 MiB,请求和响应共用 UTF-8 明文字节预算。客户端从握手自动获取上限,密文与部署代理预算由 `resolveTransportBodyLimits()` 推导(两倍明文 + 32 KiB)。握手、公钥、认证头和防重放约束独立;超限取消原流并释放 reader,请求 413、响应为加密的 500 容量错误,客户端识别 `TransportBodyTooLargeError` / `hai:crypto:070`。 [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
256
298
 
@@ -258,7 +300,7 @@ transport 默认作用于同源 `/api/*` endpoint 与 SvelteKit `__data.json`
258
300
 
259
301
  Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒,旧服务端未提供该值时仍显示校时提示。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。标准认证动作将已认证的传输时间错误转为 `HaiResult`,保留校验时间和校时提示,AuthPanel 可直接展示。
260
302
 
261
- #### 使用 `_kit.yml` 统一 transport 配置
303
+ ### 使用 `_kit.yml` 统一 transport 配置
262
304
 
263
305
  `@h-ai/kit` 提供 `KitConfigSchema` / `resolveKitConfig()`,方便应用用同一份 `_kit.yml`
264
306
  同时驱动 `hooks.server.ts` 与浏览器端 `kit.client.installBrowserTransport()`:
@@ -302,34 +344,10 @@ export function load() {
302
344
  如只在服务端读取配置,也可配合 `core.config.validate('kit', KitConfigSchema)` 使用。
303
345
  完整 `_kit.yml` 由服务端读取,可包含内部代理网段和访问策略文件路径。`+layout.server.ts` 只返回 `{ kitConfig: publicKitConfig }` 等明确公共投影,浏览器消费其中的 transport;禁止用 `?raw` 把完整配置或策略文件打入客户端,也不要在其中写入密钥。
304
346
 
305
- ### 配置
306
-
307
- `kit.createHandle()` 配置项:
308
-
309
- | 字段 | 类型 | 说明 |
310
- | ---------------- | ---------------------------------------- | ------------------------------------------------------------------ |
311
- | `auth` | `HandleAuthConfig` | 认证配置(verifyToken / protectedPaths / publicPaths / loginUrl) |
312
- | `trustedProxies` | `TrustedProxies` | 默认 `auto`;普通宿主忽略转发头,受管私有 Compose 代理采用容器网段 |
313
- | `access` | `RequestAccessConfigInput` | 默认 `false`;按路径、IP 和时间检查每次请求,可引用独立 YAML |
314
- | `rateLimit` | `object \| false` | 速率限制(windowMs / maxRequests) |
315
- | `logging` | `boolean \| object` | 请求日志(默认 `true`,`{ logBody: true }` 记录请求体) |
316
- | `crypto` | `HookCryptoConfig` | 传输加密 + Cookie 加密 |
317
- | `guards` | `GuardConfig[]` | 自定义守卫(在 auth 自动守卫之后执行) |
318
- | `middleware` | `Middleware[]` | 自定义中间件(在内置中间件之后执行) |
319
- | `a2a` | `HandleA2AOperations \| HandleA2AConfig` | A2A 协议集成(Agent Card + JSON-RPC) |
320
- | `onError` | `function` | 自定义错误处理 |
321
-
322
- 请求/响应摘要使用 `trace` 日志级别;`logging` 默认开启,`logBody` 默认关闭。日志查询从原始 `request.url` 读取,路径保留 SvelteKit 当前路由的 `event.url.pathname`,不会读取预渲染受限的 `event.url.search/searchParams`;静态页无需为构建关闭日志。查询和启用后的 JSON 正文在日志入口对 password/token/secret、授权/API Key 等字段脱敏,正文使用请求副本读取,不影响业务处理。
323
-
324
- 请求自动审计仅在 `audit.isInitialized` 时执行,纯静态站点或未选 Audit 的应用无需为 handle 初始化它。该状态在每个请求中判断,因此 handle 可先创建、模块随后初始化。选择了 Audit 的应用仍必须检查 `audit.init()` 结果并在失败时停止启动;业务显式 `audit.log/capture` 的未初始化错误不变,已初始化模块的真实写入失败仍记录 `Request audit failed`。需要强制审计的业务应检查显式审计结果,并按业务事务约束提交。
325
-
326
- 配置 `crypto.encryptedCookies` 时必须同时提供 `crypto.cookieEncryptionKey`,或设置 `HAI_KIT_COOKIE_KEY`。缺少密钥会在 `createHandle()` 时立即报错;受保护 Cookie 的明文、损坏密文或解密失败都按不存在处理,加密失败也不会回退写入明文。
327
-
328
- 密钥必须是 32 位十六进制字符串。Cookie 使用独立派生的 SM4-CBC/HMAC-SHA256 密钥,认证版本、Cookie 名、IV 与密文后才解密,防止修改或跨名称替换。升级后旧版无认证 Cookie 被视为无效,用户需重新登录;不回退接受旧格式。
329
-
330
- 原先没有运行入口和真实消费者的 Crypto Helpers 已删除,`CryptoServiceLike`、`EncryptedCookieConfig`、`CryptoCsrfConfig`、`WebhookVerifyConfig` 类型也不再导出。Cookie 保护通过上述 `kit.createHandle` 配置接入;其他加密操作使用 `@h-ai/crypto` 的真实公开契约。
347
+ ### 依赖
331
348
 
332
- 认证 Cookie 的 `Secure` 默认值在 `NODE_ENV` 或 `HAI_CORE_ENV` 为 `production` 时开启,`HAI_E2E` / `HAI_E2E_MOCK` 不降低 Cookie 安全要求。生产限流同样识别这两个环境标志;仅显式测试开关可放宽内存计数器限制。
349
+ - `@h-ai/iam` — 身份认证与授权(A2A 认证),可选;使用 A2A 功能时需在 kit 使用前初始化
350
+ - `@h-ai/crypto` — 加密(传输加密 / Cookie 加密),可选;启用加密时需在 kit 使用前初始化
333
351
 
334
352
  ### 导出路径
335
353
 
@@ -353,12 +371,6 @@ export function load() {
353
371
 
354
372
  `kit.auth.logout(cookies, accessToken)` 返回 `Promise<HaiResult<void>>`。无令牌时仅清除 Cookie 并成功;有令牌但未配置认证操作时返回失败。无论服务端结果如何都清除本地 Cookie,调用方仍必须把失败转换为错误响应或提示;仅成功结果代表服务端撤销已经确认。底层控制流异常继续传播。
355
373
 
356
- ### 错误处理
357
-
358
- - 推荐使用 `kit.handler()` 包装 API 端点。
359
- - `kit.validate.body` / `kit.guard.require` 抛出 `Response`,由 `kit.handler` 处理 API 控制流。页面 `load` 使用 `kit.guard.check` 和 SvelteKit `error`;校验查询参数用 `schema.safeParse` 后返回原生页面错误,不能直接抛 API Response。
360
- - 非控制流异常会由 `kit.handler()` 统一转换为 `500 INTERNAL_ERROR` 响应。
361
-
362
374
  ### CRUD fields
363
375
 
364
376
  声明式 CRUD 字段支持 `colSpan: 'auto' | 1 | 2`,供 UI 双列表单决定字段宽度;`validation.message` 支持字符串或 `() => string`,便于随当前语言显示校验错误。UI 布局在 `CrudPage.form` 上配置,服务端仍须独立校验。
@@ -369,7 +381,7 @@ Node 请求与 SSR 使用 `core.i18n.runWithLocale(locale, () => handleRequest()
369
381
 
370
382
  浏览器继续使用应用级 `setGlobalLocale` / `createLocaleStore`。SSR 的 UI store 初始化和 setter 只修改自身状态,不写全局语言;UI 消息从请求上下文读取。`runWithLocale` 仅支持 Node 入口,浏览器通过显式 locale 或应用状态翻译。
371
383
 
372
- ## 反馈接口与普通请求关联
384
+ ### 反馈接口与普通请求关联
373
385
 
374
386
  `kit.createFeedbackHandler` 可作为 `/api/feedback/[...path]/+server.ts` 的 GET/POST/PATCH handler,复用 api-contract schema,与 serv 使用同一数据契约;输入流受大小上限约束,客户端身份/租户字段被拒绝。宿主通过 resolveContext 从已经验证的 locals.session 映射范围和权限,null 拒绝访问,并保留现有 createHandle 的 CSRF、认证与传输加密。
375
387
 
@@ -394,51 +406,99 @@ export const POST = handler
394
406
  export const PATCH = handler
395
407
  ```
396
408
 
397
- ## 错误提示与关联日志
409
+ ### 可选交互挑战
398
410
 
399
- `kit.response.fromError/fromResult` 保留安全的字段校验 `cause`、显式公开的 `publicExt`(映射为 `ext`)和 `httpStatus`;任意内部 cause/ext 不公开。每次模块失败只写一条关联日志。
411
+ 将 kit.auth.createApi(iam).createCaptcha 导出为 POST /api/auth/captcha;kit.auth.publicPaths 已包含它。kit.client.createAuthActions() 保留标准交互请求的 captcha 字段,并提供 createCaptcha;AuthPanel 提交时自动求解。匿名首次/过期改密仍须导出 /api/auth/password/renew。服务端 IAM 检查证明,开启策略不会只依赖界面。
400
412
 
401
- HTTP/UI 边界只对明确的有效 4xx 状态公开业务提示;错误缺少 `httpStatus` 或状态非法时按 500 返回安全提示。手写业务错误须提供完整错误定义或显式状态,不能依赖缺省状态展示原文。
413
+ 策略、默认值和恢复边界见 [IAM](../iam/README.md)。
402
414
 
403
- `kit.response.validationError(errors)` 也沿用同一白名单:响应 `error.cause` 只含本地化的 `field/message`,不回显 rejectedValue、SQL 或其他额外字段,不再使用 `details.errors`。
415
+ ### 原地恢复过期会话
404
416
 
405
- 快捷响应、认证守卫和限流均使用 `HaiCommonError` 标准错误码,附带 `httpStatus`、`traceId` 与 `X-Trace-Id`。未认证返回 `UNAUTHORIZED.code`,无权限返回 `FORBIDDEN.code`,限流返回 `RATE_LIMIT_EXCEEDED.code`(429);限流存储故障返回安全的 503,原始原因只记服务端日志。
417
+ 配置 `kit.client.create({ recoverSession })` 回调可在服务端明确拒绝会话时原地重新登录并保留页面输入。使用 `@h-ai/ui` 的 `createSessionRecovery` / `SessionRecoveryDialog`:根布局绑定公开配置与原用户,回调设为 `recovery.recover`,弹窗使用标准认证 actions,销毁时 `cancel()`。登录的 `expectedUserId` 由 IAM 限定原主体;常规验证码、锁定、改密和登录限制照常执行。只对明确未认证拒绝的原请求续接一次;取消、5xx、超时和结果不明的网络写入不重放。敏感操作证明绑定旧会话时可能需要再次确认,不复用到新会话。标准认证动作显式禁用自身的恢复重试,避免错误密码递归打开弹窗。自定义认证请求应传第三个参数 `{ recoverSession: false }`。终端分组通过 terminalType(web/app/desktop/api,默认 web)传入;它不是硬件身份认证。
406
418
 
407
- HTTP 错误返回本地化安全文案及追踪编号;ToastContainer 默认隐藏编号,由排查信息图标展开和复制。响应同时包含 `error.traceId` 和 `X-Trace-Id`。框架复用本次 requestId 作为 traceId,前后端日志自动关联。底层异常、堆栈和操作上下文经凭据脱敏后写入日志,不通过 cause/ext 向用户公开。字段校验只返回本地化的字段名和错误。
419
+ ### 页面离开会话策略
408
420
 
409
- `core.error.toPublic(error, traceId?)` 用于接口/UI 边界;`core.error.formatMessage(error)` 用于重新翻译后的提示,重复调用不会重复追加标识。日志传入完整 Error 对象,不能只记录通用标题或提前 String(error) 丢失上下文。数据库失败记录驱动错误码、消息、堆栈及表/列;SQL 原文、绑定值和连接凭据不进入日志。
421
+ `session.endOnPageHide` 默认 `false`。启用后宿主调用 `actions.bindPageExit({ enabled: config.session.endOnPageHide })` 并在卸载时调用返回的清理函数;标准 admin/H5/fullstack 模板已经接入。非 BFCache 的 `pagehide`(也包括刷新、整页导航)通过 POST `/auth/session/close` 尽力撤销当前 access 及关联 refresh,沿用 CSRF/传输加密,使用 `keepalive` 且不自动刷新会话。该响应不清 Cookie/本地凭据,避免旧响应覆盖下一次登录。显式退出仍使用正常 logout,可靠撤销并清理当前凭据。浏览器可能不发送页面离开请求,因此必须按业务配合服务端 `idleTimeout`/`absoluteTimeout`;不能据此保证关闭即撤销。
410
422
 
411
- audit 已初始化时,createHandle 为业务 audit.log 绑定已验证会话的身份/角色快照、平台 getClientAddress 地址和请求编号。自动入口事件 request.execute 包含路由模板、方法、状态和成功/失败/拒绝,包含守卫跳转及 SvelteKit HTTP 控制流;不复制请求体或错误详情。capture.enabled 可关闭自动采集,capture.includeReads 可开启成功读取;默认保留失败读取、跳过成功读取。业务含义仍在应用完成/失败边界显式记录,关闭自动采集不影响业务 log。
423
+ ### 每次请求的访问控制
412
424
 
413
- ## 可选交互挑战
425
+ 路径规则的 `encryption` 可选 required / disabled,省略沿用宿主。disabled 不解密请求或加密响应,仍执行访问和身份检查;不接受框架传输信封。required 可保护自定义 JSON 端点并优先于 `excludePaths`,没有 Crypto、密钥环和 registry 时不能启用。模式冲突在创建 Handle 时拒绝。CLI 的 crypto 模板同步装配服务端和浏览器,生产选择 db 和共享数据库;策略及代理配置不下发浏览器,明文调用方按公开传输排除路径选择协议。
414
426
 
415
- 将 kit.auth.createApi(iam).createCaptcha 导出为 POST /api/auth/captcha;kit.auth.publicPaths 已包含它。kit.client.createAuthActions() 保留标准交互请求的 captcha 字段,并提供 createCaptcha;AuthPanel 提交时自动求解。匿名首次/过期改密仍须导出 /api/auth/password/renew。服务端 IAM 检查证明,开启策略不会只依赖界面。
427
+ 将服务端 `_kit.yml` 的 `trustedProxies` 和 `access` 显式传给 `kit.createHandle`。示例和生成模板在应用初始化后只创建一次 Handle。`access` 默认为 `false`;内联 `{ rules: [{ id: 'external', paths: ['/api/v1/external/**'], allowIps: ['192.0.2.0/24'] }] }` 只限制匹配接口,`global` 则限制宿主处理的全部请求。实际路径包含部署前缀;静态服务器直接返回的资源不经过 Handle。
416
428
 
417
- 策略、默认值和恢复边界见 [IAM](../iam/README.md)。
429
+ 独立文件使用 `access: { file: './config/security/request-access.yml' }`,按工作目录解析,启动读取一次,最大 1 MiB,修改后重建 Handle。全局与所有匹配规则取交集,空列表不限对应维度,时区默认 UTC。时间窗口结束后,已有会话的后续请求同样被拒绝。预检和密钥交换也执行策略;普通 transport 请求的 HTTP 403 / `hai:common:104` 保留响应加密。
418
430
 
419
- ## 原地恢复过期会话
431
+ `trustedProxies: auto` 在普通宿主不信任转发头;受管 Compose 只有后端私有且存在入口代理时才采用容器网段。自管入口显式指定代理 IP/CIDR,`[]` 可关闭。解析后的 `event.getClientAddress()` 与策略、IAM、审计使用相同地址。外层代理、不可恢复的源地址丢失、环境变量与部署回滚见[请求访问控制手册](../../docs/usage/configuration/request-access.md)。
420
432
 
421
- 配置 `kit.client.create({ recoverSession })` 回调可在服务端明确拒绝会话时原地重新登录并保留页面输入。使用 `@h-ai/ui` 的 `createSessionRecovery` / `SessionRecoveryDialog`:根布局绑定公开配置与原用户,回调设为 `recovery.recover`,弹窗使用标准认证 actions,销毁时 `cancel()`。登录的 `expectedUserId` 由 IAM 限定原主体;常规验证码、锁定、改密和登录限制照常执行。只对明确未认证拒绝的原请求续接一次;取消、5xx、超时和结果不明的网络写入不重放。敏感操作证明绑定旧会话时可能需要再次确认,不复用到新会话。标准认证动作显式禁用自身的恢复重试,避免错误密码递归打开弹窗。自定义认证请求应传第三个参数 `{ recoverSession: false }`。终端分组通过 terminalType(web/app/desktop/api,默认 web)传入;它不是硬件身份认证。
433
+ ## 配置
422
434
 
423
- ## 页面离开会话策略
435
+ ### 按场景选择配置入口
424
436
 
425
- `session.endOnPageHide` 默认 `false`。启用后宿主调用 `actions.bindPageExit({ enabled: config.session.endOnPageHide })` 并在卸载时调用返回的清理函数;标准 admin/H5/fullstack 模板已经接入。非 BFCache 的 `pagehide`(也包括刷新、整页导航)通过 POST `/auth/session/close` 尽力撤销当前 access 及关联 refresh,沿用 CSRF/传输加密,使用 `keepalive` 且不自动刷新会话。该响应不清 Cookie/本地凭据,避免旧响应覆盖下一次登录。显式退出仍使用正常 logout,可靠撤销并清理当前凭据。浏览器可能不发送页面离开请求,因此必须按业务配合服务端 `idleTimeout`/`absoluteTimeout`;不能据此保证关闭即撤销。
437
+ | 配置入口 | 场景 | 输入 | 生效结果 | 示例 |
438
+ | ----------------------------------------- | ---------------- | ---------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------- |
439
+ | `server.host / server.port` | 服务监听 | host string, port integer; HOST/PORT override | resolved Kit server config; default 127.0.0.1:3000 | `server: { host: 127.0.0.1, port: 3000 }` |
440
+ | `transport` | 同源认证加密 | false or keyExchangePath/excludePaths/byte/time/route budgets | public transport config for hook and browser; no private keys | `transport: { maxPlaintextBytes: auto }` |
441
+ | `trustedProxies` | 可信连接来源 | auto or explicit IP/CIDR list; [] disables forwarded headers | normalized client address for access policy | `trustedProxies: []` |
442
+ | `access` | 请求访问策略 | false or independent policy configuration | per-request IP/time/path checks; not business ownership | `access: false` |
443
+ | `createHandle.auth / guards / middleware` | 宿主鉴权管道 | verifyToken/operations, paths and guard/middleware callbacks | RequestEvent locals/session and rejection Response | `kit.createHandle({ auth: authOptions })` |
444
+ | `createHandle.rateLimit` | 共享限流 | windowMs, maxRequests and explicit atomic store | fixed window; 429 over quota, 503 on unavailable storage | `kit.createHandle({ rateLimit: limitOptions })` |
445
+ | `createHandle.crypto` | 注入私密运行依赖 | initialized crypto, serverKeyRing/keyStore, encryptedCookies/key | authenticated payload/cookies; ring stays server-only | `kit.createHandle({ crypto: cryptoOptions })` |
446
+ | `client.create` | 浏览器请求选项 | auth memory store, CSRF names, transport, recoverSession | same-origin apiFetch plus init/destroy/ready | `kit.client.create({ auth: true })` |
426
447
 
427
- ## 传输容量与模块预算
448
+ 使用 pnpm 构建 Svelte 应用时,工程需要在 `patchedDependencies` 注册 `deepmerge@4.3.1` 对应的补丁,并保留实际 patch 文件;采用 DaisyUI 时同样保留其样式补丁。CLI 生成工程已携带适用项。安装框架 npm 包不会把框架仓库的 pnpm 补丁配置自动传递到你的工程,手工接入应同步[补丁目录](../../patches/)及相应注册后重新安装依赖。
428
449
 
429
- 配置 `transport.maxPlaintextBytes` 支持 1 KiB~64 MiB 的显式整数或 `auto`。auto/省略在解析后为 undefined:无模块需求时仍为 1 MiB,反馈宿主使用 crypto.resolveTransportBodyLimits 的 requiredPlaintextBytes 统一推导。显式上限不足时启动/打包失败,不能被反馈覆盖;代理、adapter-node、握手与运行时使用一致预算。业务附件、解压和数据行数上限继续独立校验。
450
+ `kit.createHandle()` 配置项:
451
+
452
+ | 字段 | 类型 | 说明 |
453
+ | ---------------- | ---------------------------------------- | ------------------------------------------------------------------ |
454
+ | `auth` | `HandleAuthConfig` | 认证配置(verifyToken / protectedPaths / publicPaths / loginUrl) |
455
+ | `trustedProxies` | `TrustedProxies` | 默认 `auto`;普通宿主忽略转发头,受管私有 Compose 代理采用容器网段 |
456
+ | `access` | `RequestAccessConfigInput` | 默认 `false`;按路径、IP 和时间检查每次请求,可引用独立 YAML |
457
+ | `rateLimit` | `object \| false` | 速率限制(windowMs / maxRequests) |
458
+ | `logging` | `boolean \| object` | 请求日志(默认 `true`,`{ logBody: true }` 记录请求体) |
459
+ | `crypto` | `HookCryptoConfig` | 传输加密 + Cookie 加密 |
460
+ | `guards` | `GuardConfig[]` | 自定义守卫(在 auth 自动守卫之后执行) |
461
+ | `middleware` | `Middleware[]` | 自定义中间件(在内置中间件之后执行) |
462
+ | `a2a` | `HandleA2AOperations \| HandleA2AConfig` | A2A 协议集成(Agent Card + JSON-RPC) |
463
+ | `onError` | `function` | 自定义错误处理 |
464
+
465
+ 请求/响应摘要使用 `trace` 日志级别;`logging` 默认开启,`logBody` 默认关闭。日志查询从原始 `request.url` 读取,路径保留 SvelteKit 当前路由的 `event.url.pathname`,不会读取预渲染受限的 `event.url.search/searchParams`;静态页无需为构建关闭日志。查询和启用后的 JSON 正文在日志入口对 password/token/secret、授权/API Key 等字段脱敏,正文使用请求副本读取,不影响业务处理。
466
+
467
+ 请求自动审计仅在 `audit.isInitialized` 时执行,纯静态站点或未选 Audit 的应用无需为 handle 初始化它。该状态在每个请求中判断,因此 handle 可先创建、模块随后初始化。选择了 Audit 的应用仍必须检查 `audit.init()` 结果并在失败时停止启动;业务显式 `audit.log/capture` 的未初始化错误不变,已初始化模块的真实写入失败仍记录 `Request audit failed`。需要强制审计的业务应检查显式审计结果,并按业务事务约束提交。
468
+
469
+ 配置 `crypto.encryptedCookies` 时必须同时提供 `crypto.cookieEncryptionKey`,或设置 `HAI_KIT_COOKIE_KEY`。缺少密钥会在 `createHandle()` 时立即报错;受保护 Cookie 的明文、损坏密文或解密失败都按不存在处理,加密失败也不会回退写入明文。
470
+
471
+ 密钥必须是 32 位十六进制字符串。Cookie 使用独立派生的 SM4-CBC/HMAC-SHA256 密钥,认证版本、Cookie 名、IV 与密文后才解密,防止修改或跨名称替换。未通过认证的 Cookie 按无效处理。
472
+
473
+ 认证 Cookie 的 `Secure` 默认值在 `NODE_ENV` 或 `HAI_CORE_ENV` 为 `production` 时开启,`HAI_E2E` / `HAI_E2E_MOCK` 不降低 Cookie 安全要求。生产限流同样识别这两个环境标志;仅显式测试开关可放宽内存计数器限制。
474
+
475
+ ### 传输容量与模块预算
476
+
477
+ 配置 `transport.maxPlaintextBytes` 支持 1 KiB~64 MiB 的显式整数或 `auto`。auto/省略在解析后为 undefined:无模块需求时仍为 1 MiB,反馈宿主使用 resolveTransportBodyLimits 的 requiredPlaintextBytes 统一推导。显式上限不足时启动/打包失败,不能被反馈覆盖;代理、adapter-node、握手与运行时使用一致预算。业务附件、解压和数据行数上限继续独立校验。
430
478
 
431
479
  敏感调用通过本次 `reauthorize: () => sensitiveActions.authorize(operation)` 重新确认用途:Kit 放 apiFetch 第三参数,typed client 放 options.context。会话刷新/恢复或明确的再认证拒绝后仅续发一次;取消不写入,未提供回调不重放旧证明。
432
480
 
433
- ## 每次请求的访问控制
481
+ ## 错误处理
434
482
 
435
- 路径规则的 `encryption` 可选 required / disabled,省略沿用宿主。disabled 不解密请求或加密响应,仍执行访问和身份检查;不接受框架传输信封。required 可保护自定义 JSON 端点并优先于 `excludePaths`,没有 Crypto、密钥环和 registry 时不能启用。模式冲突在创建 Handle 时拒绝。CLI 的 crypto 模板同步装配服务端和浏览器,生产选择 db 和共享数据库;策略及代理配置不下发浏览器,明文调用方按公开传输排除路径选择协议。
483
+ - 推荐使用 `kit.handler()` 包装 API 端点。
484
+ - `kit.validate.body` / `kit.guard.require` 抛出 `Response`,由 `kit.handler` 处理 API 控制流。页面 `load` 使用 `kit.guard.check` 和 SvelteKit `error`;校验查询参数用 `schema.safeParse` 后返回原生页面错误,不能直接抛 API Response。
485
+ - 非控制流异常会由 `kit.handler()` 统一转换为 `500 INTERNAL_ERROR` 响应。
436
486
 
437
- 将服务端 `_kit.yml` 的 `trustedProxies` 和 `access` 显式传给 `kit.createHandle`。示例和生成模板在应用初始化后只创建一次 Handle。`access` 默认为 `false`;内联 `{ rules: [{ id: 'external', paths: ['/api/v1/external/**'], allowIps: ['192.0.2.0/24'] }] }` 只限制匹配接口,`global` 则限制宿主处理的全部请求。实际路径包含部署前缀;静态服务器直接返回的资源不经过 Handle。
487
+ ### 错误提示与关联日志
438
488
 
439
- 独立文件使用 `access: { file: './config/security/request-access.yml' }`,按工作目录解析,启动读取一次,最大 1 MiB,修改后重建 Handle。全局与所有匹配规则取交集,空列表不限对应维度,时区默认 UTC。时间窗口结束后,已有会话的后续请求同样被拒绝。预检和密钥交换也执行策略;普通 transport 请求的 HTTP 403 / `hai:common:104` 保留响应加密。
489
+ `kit.response.fromError/fromResult` 保留安全的字段校验 `cause`、显式公开的 `publicExt`(映射为 `ext`)和 `httpStatus`;任意内部 cause/ext 不公开。每次模块失败只写一条关联日志。
440
490
 
441
- `trustedProxies: auto` 在普通宿主不信任转发头;受管 Compose 只有后端私有且存在入口代理时才采用容器网段。自管入口显式指定代理 IP/CIDR,`[]` 可关闭。解析后的 `event.getClientAddress()` 与策略、IAM、审计使用相同地址。外层代理、不可恢复的源地址丢失、环境变量与部署回滚见[请求访问控制手册](../../docs/usage/configuration/request-access.md)。
491
+ HTTP/UI 边界只对明确的有效 4xx 状态公开业务提示;错误缺少 `httpStatus` 或状态非法时按 500 返回安全提示。手写业务错误须提供完整错误定义或显式状态,不能依赖缺省状态展示原文。
492
+
493
+ `kit.response.validationError(errors)` 也沿用同一白名单:响应 `error.cause` 只含本地化的 `field/message`,不回显 rejectedValue、SQL 或其他额外字段,字段校验从 `error.cause` 读取。
494
+
495
+ 快捷响应、认证守卫和限流均使用 `HaiCommonError` 标准错误码,附带 `httpStatus`、`traceId` 与 `X-Trace-Id`。未认证返回 `UNAUTHORIZED.code`,无权限返回 `FORBIDDEN.code`,限流返回 `RATE_LIMIT_EXCEEDED.code`(429);限流存储故障返回安全的 503,原始原因只记服务端日志。
496
+
497
+ HTTP 错误返回本地化安全文案及追踪编号;ToastContainer 默认隐藏编号,由排查信息图标展开和复制。响应同时包含 `error.traceId` 和 `X-Trace-Id`。框架复用本次 requestId 作为 traceId,前后端日志自动关联。底层异常、堆栈和操作上下文经凭据脱敏后写入日志,不通过 cause/ext 向用户公开。字段校验只返回本地化的字段名和错误。
498
+
499
+ `core.error.toPublic(error, traceId?)` 用于接口/UI 边界;`core.error.formatMessage(error)` 用于重新翻译后的提示,重复调用不会重复追加标识。日志传入完整 Error 对象,不能只记录通用标题或提前 String(error) 丢失上下文。数据库失败记录驱动错误码、消息、堆栈及表/列;SQL 原文、绑定值和连接凭据不进入日志。
500
+
501
+ audit 已初始化时,createHandle 为业务 audit.log 绑定已验证会话的身份/角色快照、平台 getClientAddress 地址和请求编号。自动入口事件 request.execute 包含路由模板、方法、状态和成功/失败/拒绝,包含守卫跳转及 SvelteKit HTTP 控制流;不复制请求体或错误详情。capture.enabled 可关闭自动采集,capture.includeReads 可开启成功读取;默认保留失败读取、跳过成功读取。业务含义仍在应用完成/失败边界显式记录,关闭自动采集不影响业务 log。
442
502
 
443
503
  ## 开发测试
444
504
 
package/dist/browser.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { B as BrowserKitFunctions } from './kit-types-BUDaKGB0.js';
2
- export { A as ApiResponse, a as AuthOperations, b as AuthOperationsProvider, C as ClientTransportConfig, c as CorsConfig, F as FormError, d as FormValidationResult, G as GuardConfig, e as GuardResult, H as HaiRequestEvent, f as HandleA2AConfig, g as HandleA2AOperations, h as HandleAuthConfig, i as HandleConfig, j as HookCryptoConfig, K as KitClient, k as KitClientConfig, l as KitConfig, m as KitConfigInput, n as KitPublicConfig, o as KitServerConfig, p as KitServerConfigInput, q as KitTransportConfig, r as KitTransportConfigInput, M as Middleware, s as MiddlewareContext, R as RateLimitConfig, t as RouteGuard, S as SessionData, u as SessionLike, T as TransportEncryptionOptions } from './kit-types-BUDaKGB0.js';
3
- export { K as KitConfigSchema, r as resolveKitConfig, a as resolveKitTransportConfig } from './kit-config-CCksTmi5.js';
1
+ import { B as BrowserKitFunctions } from './kit-types-Cr1cnB3M.js';
2
+ export { A as ApiResponse, a as AuthOperations, b as AuthOperationsProvider, C as ClientTransportConfig, c as CorsConfig, F as FormError, d as FormValidationResult, G as GuardConfig, e as GuardResult, H as HaiRequestEvent, f as HandleA2AConfig, g as HandleA2AOperations, h as HandleAuthConfig, i as HandleConfig, j as HookCryptoConfig, K as KitClient, k as KitClientConfig, l as KitConfig, m as KitConfigInput, n as KitPublicConfig, o as KitServerConfig, p as KitServerConfigInput, q as KitTransportConfig, r as KitTransportConfigInput, M as Middleware, s as MiddlewareContext, R as RateLimitConfig, t as RouteGuard, S as SessionData, u as SessionLike, T as TransportEncryptionOptions } from './kit-types-Cr1cnB3M.js';
3
+ export { K as KitConfigSchema, r as resolveKitConfig, a as resolveKitTransportConfig } from './kit-config-BQKQnu8Y.js';
4
4
  import '@h-ai/core';
5
5
  import '@h-ai/crypto';
6
6
  import '@h-ai/iam';
@@ -1,4 +1,4 @@
1
- export { C as ClientTransportConfig, K as KitClient, k as KitClientConfig, v as createKitAuthActions, w as createKitClient, x as installBrowserTransport, y as installBrowserTransportFetch } from '../kit-types-BUDaKGB0.js';
1
+ export { C as ClientTransportConfig, K as KitClient, k as KitClientConfig, v as createKitAuthActions, w as createKitClient, x as installBrowserTransport, y as installBrowserTransportFetch } from '../kit-types-Cr1cnB3M.js';
2
2
  import '@h-ai/core';
3
3
  import '@h-ai/crypto';
4
4
  import '@h-ai/iam';
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { S as SessionData, z as KitFunctions } from './kit-types-BUDaKGB0.js';
2
- export { A as ApiResponse, D as AuthCookieWriter, a as AuthOperations, b as AuthOperationsProvider, B as BrowserKitFunctions, C as ClientTransportConfig, c as CorsConfig, E as CrudApiDef, I as CrudFieldDef, J as CrudFieldOption, L as CrudFieldOptions, N as CrudFieldType, O as CrudFieldValidation, P as CrudFilterParams, Q as CrudOperations, U as CrudPaginatedResult, V as CrudPaginationParams, W as CrudResourceDef, X as CrudTableColumn, Y as CrudText, Z as FeedbackHandlerOptions, F as FormError, d as FormValidationResult, G as GuardConfig, e as GuardResult, H as HaiRequestEvent, f as HandleA2AConfig, g as HandleA2AOperations, h as HandleAuthConfig, i as HandleConfig, j as HookCryptoConfig, _ as IdParamSchema, K as KitClient, k as KitClientConfig, l as KitConfig, m as KitConfigInput, n as KitPublicConfig, o as KitServerConfig, p as KitServerConfigInput, q as KitTransportConfig, r as KitTransportConfigInput, $ as MemoryRateLimitStore, M as Middleware, s as MiddlewareContext, a0 as PaginationQuerySchema, a1 as PermissionGuardConfig, a2 as RateLimitCache, R as RateLimitConfig, a3 as RateLimitEntry, a4 as RateLimitStore, t as RouteGuard, u as SessionLike, T as TransportEncryptionOptions, a5 as defineCrud, a6 as matchPermission, a7 as resolveOptions, a8 as resolveText } from './kit-types-BUDaKGB0.js';
1
+ import { S as SessionData, z as KitFunctions } from './kit-types-Cr1cnB3M.js';
2
+ export { A as ApiResponse, D as AuthCookieWriter, a as AuthOperations, b as AuthOperationsProvider, B as BrowserKitFunctions, C as ClientTransportConfig, c as CorsConfig, E as CrudApiDef, I as CrudFieldDef, J as CrudFieldOption, L as CrudFieldOptions, N as CrudFieldType, O as CrudFieldValidation, P as CrudFilterParams, Q as CrudOperations, U as CrudPaginatedResult, V as CrudPaginationParams, W as CrudResourceDef, X as CrudTableColumn, Y as CrudText, Z as FeedbackHandlerOptions, F as FormError, d as FormValidationResult, G as GuardConfig, e as GuardResult, H as HaiRequestEvent, f as HandleA2AConfig, g as HandleA2AOperations, h as HandleAuthConfig, i as HandleConfig, j as HookCryptoConfig, _ as IdParamSchema, K as KitClient, k as KitClientConfig, l as KitConfig, m as KitConfigInput, n as KitPublicConfig, o as KitServerConfig, p as KitServerConfigInput, q as KitTransportConfig, r as KitTransportConfigInput, $ as MemoryRateLimitStore, M as Middleware, s as MiddlewareContext, a0 as PaginationQuerySchema, a1 as PermissionGuardConfig, a2 as RateLimitCache, R as RateLimitConfig, a3 as RateLimitEntry, a4 as RateLimitStore, t as RouteGuard, u as SessionLike, T as TransportEncryptionOptions, a5 as defineCrud, a6 as matchPermission, a7 as resolveOptions, a8 as resolveText } from './kit-types-Cr1cnB3M.js';
3
3
  import { CryptoFunctions } from '@h-ai/crypto';
4
4
  export { EncryptedPayload, TransportEncryptionManager, TransportKeyPair } from '@h-ai/crypto';
5
- export { A as A2AApiKeyAuthConfig, K as KitA2AHandlerConfig, R as ResolvedA2AConfig } from './kit-a2a-types-CeDikRAX.js';
5
+ export { A as A2AApiKeyAuthConfig, K as KitA2AHandlerConfig, R as ResolvedA2AConfig } from './kit-a2a-types-sw7sI-ZT.js';
6
6
  export { T as TransportEncryptionConfig } from './kit-crypto-types-CqFENBqm.js';
7
- export { K as KitConfigSchema, r as resolveKitConfig, a as resolveKitTransportConfig } from './kit-config-CCksTmi5.js';
7
+ export { K as KitConfigSchema, r as resolveKitConfig, a as resolveKitTransportConfig } from './kit-config-BQKQnu8Y.js';
8
8
  import '@h-ai/core';
9
9
  import '@h-ai/iam';
10
10
  import '@sveltejs/kit';