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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.en.md +39 -13
  2. package/README.ja.md +39 -13
  3. package/README.md +39 -13
  4. package/dist/adapter/index.d.ts +17 -2
  5. package/dist/adapter/index.js.map +1 -1
  6. package/dist/browser.d.ts +12 -6
  7. package/dist/browser.js +6 -7
  8. package/dist/browser.js.map +1 -1
  9. package/dist/{chunk-DVW7CQ3H.js → chunk-2PWGJCWX.js} +3 -3
  10. package/dist/chunk-2PWGJCWX.js.map +1 -0
  11. package/dist/{chunk-VOTDAVCB.js → chunk-2YYRP7HH.js} +17 -33
  12. package/dist/chunk-2YYRP7HH.js.map +1 -0
  13. package/dist/{chunk-MLSP67JF.js → chunk-333FGMGQ.js} +59 -111
  14. package/dist/chunk-333FGMGQ.js.map +1 -0
  15. package/dist/chunk-OVRZIDCD.js +175 -0
  16. package/dist/chunk-OVRZIDCD.js.map +1 -0
  17. package/dist/{chunk-L7BTMAKX.js → chunk-P6XH7G4N.js} +3 -6
  18. package/dist/chunk-P6XH7G4N.js.map +1 -0
  19. package/dist/{chunk-NVYIUBU7.js → chunk-XC7I2L3G.js} +2 -2
  20. package/dist/chunk-XC7I2L3G.js.map +1 -0
  21. package/dist/client/index.d.ts +47 -1
  22. package/dist/client/index.js +3 -3
  23. package/dist/client/index.js.map +1 -1
  24. package/dist/index.d.ts +217 -347
  25. package/dist/index.js +138 -83
  26. package/dist/index.js.map +1 -1
  27. package/dist/kit-a2a-types-CwT1AFQu.d.ts +136 -0
  28. package/dist/{index-B7s-0mZl.d.ts → kit-client-auth-HnZIYUia.d.ts} +220 -101
  29. package/dist/kit-crypto-types-Dd24aJ4m.d.ts +194 -0
  30. package/dist/{kit-i18n-D9Qqk-1k.d.ts → kit-i18n-DEzmzE5T.d.ts} +142 -29
  31. package/dist/{kit-types-ByWdbCeO.d.ts → kit-types-CnzhN-AS.d.ts} +240 -162
  32. package/dist/modules/a2a/index.d.ts +13 -111
  33. package/dist/modules/a2a/index.js +2 -2
  34. package/dist/modules/crypto/index.d.ts +28 -195
  35. package/dist/modules/crypto/index.js +3 -3
  36. package/dist/modules/crypto/index.js.map +1 -1
  37. package/package.json +10 -10
  38. package/dist/chunk-DVW7CQ3H.js.map +0 -1
  39. package/dist/chunk-L7BTMAKX.js.map +0 -1
  40. package/dist/chunk-MLSP67JF.js.map +0 -1
  41. package/dist/chunk-NVYIUBU7.js.map +0 -1
  42. package/dist/chunk-QHYNL24G.js +0 -117
  43. package/dist/chunk-QHYNL24G.js.map +0 -1
  44. package/dist/chunk-VOTDAVCB.js.map +0 -1
package/dist/browser.js CHANGED
@@ -1,8 +1,8 @@
1
- import { defineCrud } from './chunk-L7BTMAKX.js';
2
- export { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig } from './chunk-L7BTMAKX.js';
3
- import { createTokenStore, clearBrowserToken, setBrowserToken, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient } from './chunk-VOTDAVCB.js';
4
- import './chunk-NVYIUBU7.js';
5
- import { setAllModulesLocale } from './chunk-QHYNL24G.js';
1
+ import { defineCrud } from './chunk-P6XH7G4N.js';
2
+ export { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig } from './chunk-P6XH7G4N.js';
3
+ import { clearBrowserToken, setBrowserToken, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient } from './chunk-2YYRP7HH.js';
4
+ import './chunk-XC7I2L3G.js';
5
+ import { setAllModulesLocale } from './chunk-OVRZIDCD.js';
6
6
 
7
7
  // src/browser.ts
8
8
  var kit = {
@@ -17,8 +17,7 @@ var kit = {
17
17
  },
18
18
  auth: {
19
19
  setBrowserToken,
20
- clearBrowserToken,
21
- createTokenStore
20
+ clearBrowserToken
22
21
  },
23
22
  i18n: {
24
23
  setLocale: setAllModulesLocale
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/browser.ts"],"names":[],"mappings":";;;;;;;AAiBO,IAAM,GAAA,GAAM;AAAA,EACjB,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ,eAAA;AAAA,IACR,iBAAA,EAAmB,oBAAA;AAAA,IACnB,uBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ;AAAA,GACV;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,eAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,SAAA,EAAW;AAAA;AAEf","file":"browser.js","sourcesContent":["/**\n * @h-ai/kit — 浏览器入口\n *\n * 为客户端提供精简且安全的运行时导出,避免将服务端 Hook/A2A/IAM 运行时代码打入浏览器包。\n * @module browser\n */\n\nimport { createKitAuthActions } from './client/kit-client-auth.js'\nimport { createKitClient, installBrowserTransport, installBrowserTransportFetch } from './client/kit-client.js'\nimport { defineCrud } from './crud/kit-crud.js'\nimport { clearBrowserToken, createTokenStore, setBrowserToken } from './kit-auth.js'\nimport { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig } from './kit-config.js'\nimport { setAllModulesLocale } from './kit-i18n.js'\n\n/**\n * 浏览器端 kit 对象(仅暴露 client-safe 能力)\n */\nexport const kit = {\n client: {\n create: createKitClient,\n createAuthActions: createKitAuthActions,\n installBrowserTransport,\n installBrowserTransportFetch,\n },\n crud: {\n define: defineCrud,\n },\n auth: {\n setBrowserToken,\n clearBrowserToken,\n createTokenStore,\n },\n i18n: {\n setLocale: setAllModulesLocale,\n },\n}\n\nexport { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig }\n\nexport type * from './client/index.js'\nexport type * from './kit-types.js'\n"]}
1
+ {"version":3,"sources":["../src/browser.ts"],"names":[],"mappings":";;;;;;;AAwBO,IAAM,GAAA,GAAM;AAAA,EACjB,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ,eAAA;AAAA,IACR,iBAAA,EAAmB,oBAAA;AAAA,IACnB,uBAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ;AAAA,GACV;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,eAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,SAAA,EAAW;AAAA;AAEf","file":"browser.js","sourcesContent":["/**\n * @h-ai/kit — 浏览器入口\n *\n * 为客户端提供精简且安全的运行时导出,避免将服务端 Hook/A2A/IAM 运行时代码打入浏览器包。\n * @module browser\n */\n\nimport { createKitAuthActions } from './client/kit-client-auth.js'\nimport { createKitClient, installBrowserTransport, installBrowserTransportFetch } from './client/kit-client.js'\nimport { defineCrud } from './crud/kit-crud.js'\nimport { clearBrowserToken, setBrowserToken } from './kit-auth.js'\nimport { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig } from './kit-config.js'\nimport { setAllModulesLocale } from './kit-i18n.js'\n\n/**\n * 浏览器端 kit 对象(仅暴露 client-safe 能力)\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * const client = kit.client.create({ auth: true })\n * client.destroy()\n * ```\n */\nexport const kit = {\n client: {\n create: createKitClient,\n createAuthActions: createKitAuthActions,\n installBrowserTransport,\n installBrowserTransportFetch,\n },\n crud: {\n define: defineCrud,\n },\n auth: {\n setBrowserToken,\n clearBrowserToken,\n },\n i18n: {\n setLocale: setAllModulesLocale,\n },\n}\n\nexport { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig }\n\nexport type {\n ClientTransportConfig,\n KitClient,\n KitClientConfig,\n} from './client/index.js'\nexport type {\n ApiResponse,\n AuthOperations,\n AuthOperationsProvider,\n CorsConfig,\n FormError,\n FormValidationResult,\n GuardConfig,\n GuardResult,\n HaiRequestEvent,\n HandleA2AConfig,\n HandleA2AOperations,\n HandleAuthConfig,\n HandleConfig,\n HookCryptoConfig,\n KitConfig,\n KitConfigInput,\n KitServerConfig,\n KitServerConfigInput,\n KitTransportConfig,\n KitTransportConfigInput,\n Middleware,\n MiddlewareContext,\n RateLimitConfig,\n RouteGuard,\n SessionData,\n SessionLike,\n TransportEncryptionOptions,\n} from './kit-types.js'\n"]}
@@ -1,4 +1,4 @@
1
- import { kitM } from './chunk-QHYNL24G.js';
1
+ import { kitM } from './chunk-OVRZIDCD.js';
2
2
  import { iam } from '@h-ai/iam';
3
3
 
4
4
  function createA2AApiKeyAuthenticator(config) {
@@ -137,5 +137,5 @@ async function handleA2ARequest(event, requestId, config) {
137
137
  }
138
138
 
139
139
  export { createA2AApiKeyAuthenticator, createA2AHandler, createAgentCardHandler, handleA2ARequest, resolveA2AConfig };
140
- //# sourceMappingURL=chunk-DVW7CQ3H.js.map
141
- //# sourceMappingURL=chunk-DVW7CQ3H.js.map
140
+ //# sourceMappingURL=chunk-2PWGJCWX.js.map
141
+ //# sourceMappingURL=chunk-2PWGJCWX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/modules/a2a/kit-a2a-auth.ts","../src/modules/a2a/kit-a2a-helpers.ts","../src/modules/a2a/kit-a2a-handle.ts"],"names":[],"mappings":"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","file":"chunk-2PWGJCWX.js","sourcesContent":["/**\n * @h-ai/kit — A2A API Key 认证器\n *\n * 从请求头或 query 参数中提取 API Key,调用 IAM apiKey.verifyApiKey 验证。\n * @module kit-a2a-auth\n */\n\nimport type { RequestEvent } from '@sveltejs/kit'\nimport { iam } from '@h-ai/iam'\n\n/**\n * A2A API Key 认证器配置\n * @example\n * ```ts\n * import type { A2AApiKeyAuthConfig } from '@h-ai/kit'\n *\n * const value: A2AApiKeyAuthConfig = { in: 'header', name: 'X-Agent-Key' }\n * void value\n * ```\n */\nexport interface A2AApiKeyAuthConfig {\n /** API Key 的传递位置 */\n in: 'header' | 'query'\n /** 参数名 */\n name: string\n}\n\n/**\n * 创建 A2A API Key 认证函数\n *\n * 根据配置从请求头或 query 参数中提取 API Key,\n * 通过 `iam.apiKey.verifyApiKey()` 验证后返回调用方上下文。\n *\n * - 无 API Key → 返回 `null`(由调用方拒绝请求)\n * - 验证失败 → 返回 `null`(拒绝)\n * - 验证成功 → 返回 `{ agentId, apiKeyId, scopes }`\n *\n * @param config - API Key 提取配置\n * @returns authenticate 函数,可直接赋给 HandleA2AConfig.authenticate\n * @example 模块内部上下文\n * ```ts\n * const authenticate = createA2AApiKeyAuthenticator({ in: 'header', name: 'X-Agent-Key' })\n * // 应用先初始化 IAM;拒绝返回 null,交给路由转为 401。\n * void authenticate\n * ```\n */\nexport function createA2AApiKeyAuthenticator(\n config: A2AApiKeyAuthConfig,\n): (event: RequestEvent) => Promise<Record<string, unknown> | null> {\n return async (event: RequestEvent) => {\n const rawKey = config.in === 'header'\n ? event.request.headers.get(config.name)\n : event.url.searchParams.get(config.name)\n\n if (!rawKey)\n return null\n\n const result = await iam.apiKey.verifyApiKey(rawKey)\n if (!result.success)\n return null\n\n return {\n agentId: result.data.userId,\n apiKeyId: result.data.id,\n scopes: result.data.scopes,\n }\n }\n}\n","/**\n * @h-ai/kit — A2A 路由处理器\n *\n * 提供 SvelteKit 路由工厂:Agent Card 端点和 JSON-RPC 处理端点。\n * @module kit-a2a-helpers\n */\n\nimport type { RequestEvent, RequestHandler } from '@sveltejs/kit'\n\nimport type { KitA2AHandlerConfig } from './kit-a2a-types.js'\nimport { kitM } from '../../kit-i18n.js'\nimport { createA2AApiKeyAuthenticator } from './kit-a2a-auth.js'\n\n/**\n * 创建 Agent Card GET 处理器\n *\n * 返回当前 Agent 的 Agent Card(JSON),通常挂载在 `/.well-known/agent-card.json`。\n *\n * @param getAgentCard - 获取 Agent Card 的回调(通常调用 `ai.a2a.getAgentCard()`)\n * @returns SvelteKit GET 处理器\n * @example 模块内部上下文\n * ```ts\n * const handler = createAgentCardHandler(() => ({ name: 'Example agent', url: 'https://agent.example.com/a2a' }))\n * void handler\n * ```\n */\nexport function createAgentCardHandler(\n getAgentCard: () => object,\n): RequestHandler {\n return async () => {\n const card = getAgentCard()\n return new Response(JSON.stringify(card), {\n headers: { 'Content-Type': 'application/json' },\n })\n }\n}\n\n/**\n * 创建 A2A JSON-RPC POST 处理器\n *\n * 将 SvelteKit Request 转换为 JSON-RPC body,委托给 `handleRequest` 回调处理。\n * 支持单条响应和流式(SSE)响应。\n *\n * @param handleRequest - JSON-RPC 请求处理回调(通常调用 `ai.a2a.handleRequest()`)\n * @param config - 可选配置(认证等)\n * @returns SvelteKit POST 处理器\n * @example 模块内部上下文\n * ```ts\n * function build(operations: Parameters<typeof createA2AHandler>[0]) {\n * return createA2AHandler(operations, { authenticate: async () => null })\n * }\n * ```\n */\nexport function createA2AHandler(\n handleRequest: (body: unknown, context?: Record<string, unknown>) => Promise<{\n streaming: boolean\n body?: unknown\n stream?: AsyncGenerator<unknown, void, undefined>\n }>,\n config?: KitA2AHandlerConfig,\n): RequestHandler {\n return async (event: RequestEvent) => {\n // 可选认证\n let context: Record<string, unknown> | undefined\n if (config?.authenticate) {\n const authFn = typeof config.authenticate === 'function'\n ? config.authenticate\n : createA2AApiKeyAuthenticator({ in: 'header', name: 'x-api-key' })\n const authResult = await authFn(event)\n if (!authResult)\n return Response.json({ error: { code: 'A2A_AUTH_FAILED', message: kitM('kit_a2aAuthFailed') } }, { status: 401 })\n context = authResult\n }\n\n // 解析 JSON-RPC 请求体\n let requestBody: unknown\n try {\n requestBody = await event.request.json()\n }\n catch {\n return Response.json({ jsonrpc: '2.0', id: null, error: { code: -32700, message: kitM('kit_a2aParseError') } }, { status: 400 })\n }\n const result = await handleRequest(requestBody, context)\n\n if (result.streaming && result.stream) {\n // 流式响应:SSE 格式\n const stream = result.stream\n const readableStream = new ReadableStream({\n // 按下游拉取推进迭代,避免 start 中无界预读;断开时释放生成器。\n async pull(controller) {\n try {\n const chunk = await stream.next()\n if (chunk.done)\n controller.close()\n else\n controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(chunk.value)}\\n\\n`))\n }\n catch (error) {\n controller.error(error)\n }\n },\n async cancel() {\n await stream.return()\n },\n })\n\n return new Response(readableStream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n })\n }\n\n // 单条响应\n return new Response(JSON.stringify(result.body), {\n headers: { 'Content-Type': 'application/json' },\n })\n }\n}\n","/**\n * @h-ai/kit — A2A Handle 拦截器\n *\n * 在 SvelteKit Handle Hook 中拦截 A2A 端点(Agent Card 发现 + JSON-RPC 处理),\n * 自动处理请求并返回 Response,不匹配时返回 null 由 Handle 继续后续流程。\n * @module kit-a2a-handle\n */\n\nimport type { RequestEvent } from '@sveltejs/kit'\nimport type { HandleA2AConfig, HandleA2AOperations, HandleConfig } from '../../kit-types.js'\nimport { kitM } from '../../kit-i18n.js'\nimport { createA2AApiKeyAuthenticator } from './kit-a2a-auth.js'\nimport { createA2AHandler } from './kit-a2a-helpers.js'\n\n// ─── 解析后的 A2A 内部配置 ───\n\n/**\n * A2A 解析后的内部配置\n * @example\n * ```ts\n * import type { ResolvedA2AConfig } from '@h-ai/kit'\n *\n * function paths(config: ResolvedA2AConfig) { return [config.cardPath, config.rpcPath] }\n * ```\n */\nexport interface ResolvedA2AConfig {\n operations: HandleA2AOperations\n cardPath: string\n rpcPath: string\n authenticate?: (event: RequestEvent) => Promise<Record<string, unknown> | null | undefined>\n}\n\n// ─── A2A 配置解析 ───\n\n/**\n * 解析 A2A 配置(简单模式 / 配置模式)\n *\n * - 简单模式:直接传入 `ai.a2a` 操作对象,使用默认路径\n * - 配置模式:传入 `{ operations, rpcPath?, cardPath?, authenticate? }`\n *\n * @param input - HandleConfig.a2a 配置\n * @returns 解析后的配置,或 `null` 表示未配置\n * @example 模块内部上下文\n * ```ts\n * function resolve(operations: HandleA2AOperations) {\n * return resolveA2AConfig({ operations, rpcPath: '/a2a', authenticate: async () => null })\n * }\n * ```\n */\nexport function resolveA2AConfig(\n input: HandleConfig['a2a'],\n): ResolvedA2AConfig | null {\n if (!input)\n return null\n\n // 简单模式:直接传入操作对象\n if ('getAgentCard' in input && 'handleRequest' in input) {\n return {\n operations: input as HandleA2AOperations,\n cardPath: '/.well-known/agent-card.json',\n rpcPath: '/a2a',\n }\n }\n\n // 配置模式:带 operations 字段\n const cfg = input as HandleA2AConfig\n return {\n operations: cfg.operations,\n cardPath: cfg.cardPath ?? '/.well-known/agent-card.json',\n rpcPath: cfg.rpcPath ?? '/a2a',\n authenticate: resolveAuthenticate(cfg.authenticate, cfg.operations),\n }\n}\n\n/**\n * 解析 authenticate 配置\n *\n * - `undefined` → 无认证\n * - `'apiKey'` → 从 Agent Card security 配置创建 API Key 认证器\n * - 函数 → 直接使用\n */\nfunction resolveAuthenticate(\n authenticate: HandleA2AConfig['authenticate'],\n operations: HandleA2AOperations,\n): ResolvedA2AConfig['authenticate'] {\n if (!authenticate)\n return undefined\n\n if (typeof authenticate === 'function')\n return authenticate\n\n // 'apiKey' 快捷方式:从 Agent Card 获取 security 配置\n const cardResult = operations.getAgentCard()\n const securitySchemes = cardResult.success && cardResult.data\n ? (cardResult.data as { securitySchemes?: Record<string, { type: string, in?: string, name?: string }> }).securitySchemes\n : undefined\n const apiKeyCfg = securitySchemes?.apiKey\n\n return createA2AApiKeyAuthenticator({\n in: apiKeyCfg?.in === 'query' ? 'query' : 'header',\n name: apiKeyCfg?.name ?? 'x-api-key',\n })\n}\n\n// ─── A2A 请求处理 ───\n\n/**\n * 处理 A2A 端点请求\n *\n * 匹配 Agent Card GET 和 JSON-RPC POST 端点,返回 Response。\n * 不匹配时返回 `null`,由 Handle 继续后续流程。\n *\n * @param event - SvelteKit RequestEvent\n * @param requestId - 当前请求 ID(用于响应头)\n * @param config - 解析后的 A2A 配置\n * @returns 匹配时返回 Response,不匹配返回 null\n * @example 模块内部上下文\n * ```ts\n * async function handle(event: RequestEvent, config: ResolvedA2AConfig) {\n * return handleA2ARequest(event, 'req_1', config) // null 表示路径未匹配,交给后续 handle。\n * }\n * ```\n */\nexport async function handleA2ARequest(\n event: RequestEvent,\n requestId: string,\n config: ResolvedA2AConfig,\n): Promise<Response | null> {\n const { pathname } = event.url\n\n // Agent Card 发现端点\n if (pathname === config.cardPath && event.request.method === 'GET') {\n const result = config.operations.getAgentCard()\n if (result.success) {\n return new Response(JSON.stringify(result.data), {\n headers: { 'Content-Type': 'application/json', 'X-Request-Id': requestId },\n })\n }\n return new Response(\n JSON.stringify({ error: { code: 'A2A_NOT_CONFIGURED', message: kitM('kit_a2aCardUnavailable') } }),\n { status: 503, headers: { 'Content-Type': 'application/json', 'X-Request-Id': requestId } },\n )\n }\n\n // A2A JSON-RPC 端点\n if (pathname === config.rpcPath && event.request.method === 'POST') {\n // Hook 与显式路由共享认证、JSON-RPC 错误和流式生命周期逻辑。\n const handler = createA2AHandler(\n (body, context) => config.operations.handleRequest(body, context),\n { authenticate: config.authenticate },\n )\n const response = await handler(event)\n response.headers.set('X-Request-Id', requestId)\n return response\n }\n\n return null\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { resolveTransportPath, DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, shouldUseTransportForUrl } from './chunk-NVYIUBU7.js';
2
- import { kitM } from './chunk-QHYNL24G.js';
1
+ import { resolveTransportPath, DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, shouldUseTransportForUrl } from './chunk-XC7I2L3G.js';
2
+ import { kitM } from './chunk-OVRZIDCD.js';
3
3
  import process from 'process';
4
4
  import { ok, err } from '@h-ai/core';
5
5
  import { TRANSPORT_PROTOCOL } from '@h-ai/crypto';
@@ -53,7 +53,8 @@ function setToken(cookies, token, maxAge) {
53
53
  path: "/",
54
54
  httpOnly: true,
55
55
  sameSite: "lax",
56
- secure: process.env.NODE_ENV === "production",
56
+ // 测试开关仅放宽共享状态要求,不降低生产凭证 Cookie 的传输安全。
57
+ secure: process.env.NODE_ENV === "production" || process.env.HAI_CORE_ENV === "production",
57
58
  maxAge
58
59
  });
59
60
  }
@@ -97,33 +98,16 @@ async function registerAndLogin(cookies, options) {
97
98
  return runAuthLogin(cookies, options, (ops, input) => ops.registerAndLogin(input));
98
99
  }
99
100
  async function logout(cookies, accessToken) {
100
- const ops = getAuthOperations();
101
- if (accessToken && ops) {
102
- await ops.logout(accessToken);
101
+ try {
102
+ if (!accessToken)
103
+ return ok(void 0);
104
+ const ops = getAuthOperations();
105
+ if (!ops)
106
+ return authNotConfiguredError();
107
+ return await ops.logout(accessToken);
108
+ } finally {
109
+ clearToken(cookies);
103
110
  }
104
- clearToken(cookies);
105
- }
106
- function createTokenStore(key = DEFAULT_TOKEN_COOKIE_NAME) {
107
- return {
108
- get() {
109
- if (typeof window === "undefined") {
110
- return null;
111
- }
112
- return window.localStorage.getItem(key);
113
- },
114
- set(token) {
115
- if (typeof window === "undefined") {
116
- return;
117
- }
118
- window.localStorage.setItem(key, token);
119
- },
120
- clear() {
121
- if (typeof window === "undefined") {
122
- return;
123
- }
124
- window.localStorage.removeItem(key);
125
- }
126
- };
127
111
  }
128
112
  function setBrowserToken(token) {
129
113
  defaultBrowserTokenStore.set(token);
@@ -286,7 +270,7 @@ function createKitAuthActions(options = {}) {
286
270
  return post("login/ldap", { username: input.identifier, password: input.password });
287
271
  if (input.method === "email" || input.method === "sms")
288
272
  return post("login/otp", { identifier: input.identifier, code: input.code });
289
- return post("login", { identifier: input.identifier, password: input.password, rememberMe: input.rememberMe });
273
+ return post("login", { identifier: input.identifier, password: input.password });
290
274
  },
291
275
  register: (input) => post("register", { ...input, confirmPassword: input.password }),
292
276
  sendOtp: (identifier) => post("otp/send", { identifier }),
@@ -295,6 +279,6 @@ function createKitAuthActions(options = {}) {
295
279
  };
296
280
  }
297
281
 
298
- export { clearBrowserToken, configureAuth, createKitAuthActions, createKitClient, createTokenStore, getAccessToken, installBrowserTransport, installBrowserTransportFetch, login, loginWithApiKey, loginWithLdap, loginWithOtp, logout, registerAndLogin, setBrowserToken };
299
- //# sourceMappingURL=chunk-VOTDAVCB.js.map
300
- //# sourceMappingURL=chunk-VOTDAVCB.js.map
282
+ export { clearBrowserToken, configureAuth, createKitAuthActions, createKitClient, getAccessToken, installBrowserTransport, installBrowserTransportFetch, login, loginWithApiKey, loginWithLdap, loginWithOtp, logout, registerAndLogin, setBrowserToken };
283
+ //# sourceMappingURL=chunk-2YYRP7HH.js.map
284
+ //# sourceMappingURL=chunk-2YYRP7HH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kit-auth.ts","../src/client/kit-client.ts","../src/client/kit-client-auth.ts"],"names":["ok"],"mappings":"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","file":"chunk-2YYRP7HH.js","sourcesContent":["/**\n * @h-ai/kit — Bearer 认证工具\n *\n * 提供服务端认证 Cookie 管理(login / logout)与浏览器端 Token 存储工具。\n * @module kit-auth\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ApiKeyCredentials, AuthResult, LdapCredentials, OtpCredentials, PasswordCredentials, RegisterOptions } from '@h-ai/iam'\nimport type { AuthOperations, AuthOperationsProvider } from './kit-types.js'\nimport process from 'node:process'\nimport { err, ok } from '@h-ai/core'\nimport { kitM } from './kit-i18n.js'\n\n/** 默认 Token Cookie 名 */\nconst DEFAULT_TOKEN_COOKIE_NAME = 'hai_access_token'\n\n/**\n * 应用级认证配置(由 createHandle 一次性初始化,生命周期同进程)\n *\n * 此状态非请求级:仅在 createHandle() 时写入一次,后续所有请求只读访问。\n */\nconst authState = {\n cookieName: DEFAULT_TOKEN_COOKIE_NAME,\n operations: null as AuthOperationsProvider | null,\n}\n\n/**\n * 配置认证参数\n *\n * 通常由 `createHandle` 在初始化时自动调用,无需手动调用。\n * @param config - 启动阶段注入的 Cookie 名和认证操作提供器。\n * @param config.cookieName - 认证 Cookie 名。\n * @param config.operations - 已初始化服务的认证操作或惰性提供器。\n * @example 模块内部上下文\n * ```ts\n * function configure(operations: AuthOperationsProvider) {\n * configureAuth({ cookieName: 'hai_access_token', operations })\n * }\n * ```\n */\nexport function configureAuth(config: { cookieName?: string, operations?: AuthOperationsProvider }): void {\n if (config.cookieName)\n authState.cookieName = config.cookieName\n if (config.operations)\n authState.operations = config.operations\n}\n\n/**\n * 服务端认证只读 Cookie 依赖;由当前请求提供,不能跨请求缓存。\n * @example 模块内部上下文\n * ```ts\n * const cookies: CookieReader = { get: name => name === 'hai_access_token' ? 'example-only-token' : undefined }\n * const token = getAccessToken(new Request('https://app.example.com/api'), cookies)\n * void token\n * ```\n */\ninterface CookieReader {\n get: (name: string) => string | undefined\n}\n\n/**\n * 服务端认证写入/删除 Cookie 依赖;由当前请求提供并执行安全属性。\n * @example 模块内部上下文\n * ```ts\n * function signIn(cookies: CookieWriter, credentials: PasswordCredentials) {\n * return login(cookies, credentials) // 业务调用者检查 success 后消费身份。\n * }\n * ```\n */\ninterface CookieWriter {\n set: (name: string, value: string, options: {\n path: string\n httpOnly: boolean\n sameSite: 'lax' | 'strict' | 'none'\n secure: boolean\n maxAge?: number\n }) => void\n delete: (name: string, options: { path: string }) => void\n}\n\n/**\n * 浏览器 Token 存储器;使用页面内存或平台安全存储,禁止写入 Web Storage。\n * @example 模块内部上下文\n * ```ts\n * const store: BrowserTokenStore = getDefaultBrowserTokenStore()\n * store.clear() // 当前页面内存状态;不持有服务端会话真值。\n * ```\n */\nexport interface BrowserTokenStore {\n get: () => string | null\n set: (token: string) => void\n clear: () => void\n}\n\nfunction createMemoryTokenStore(): BrowserTokenStore {\n let token: string | null = null\n return {\n get: () => token,\n set: (value: string) => { token = value },\n clear: () => { token = null },\n }\n}\n\n// 默认浏览器 Token 只保存在当前页面内存中,避免 auth: true 隐式落到 localStorage。\nconst defaultBrowserTokenStore = createMemoryTokenStore()\n\n/**\n * 获取默认浏览器 Token 存储(页面内存,不持久化)。\n * @returns 当前页面的内存令牌存储器;不持有服务端共享状态。\n * @example 模块内部上下文\n * ```ts\n * const store = getDefaultBrowserTokenStore()\n * store.set('example-only-token')\n * store.clear()\n * ```\n */\nexport function getDefaultBrowserTokenStore(): BrowserTokenStore {\n return defaultBrowserTokenStore\n}\n\n// ─── 内部工具(仅 kit 包内使用) ───\n\n/**\n * 从请求 Authorization 头提取 Bearer Token。\n */\nfunction getBearerTokenFromRequest(request: Request): string | null {\n const authorization = request.headers.get('authorization')\n if (!authorization)\n return null\n\n const [scheme, token] = authorization.split(' ')\n if (!scheme || !token)\n return null\n\n if (scheme.toLowerCase() !== 'bearer')\n return null\n\n const trimmed = token.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\n/**\n * 从请求中统一提取 Access Token(Bearer 优先,其次配置的 Cookie 名)。\n * @param request - 当前 HTTP 请求,优先读取 Authorization Bearer。\n * @param cookies - 当前请求的 Cookie 读取器;未提供时仅检查请求头。\n * @returns 解析到的令牌;均无值时为 null。\n * @example 模块内部上下文\n * ```ts\n * const request = new Request('https://app.example.com/api', { headers: { Authorization: 'Bearer example-only-token' } })\n * const token = getAccessToken(request)\n * void token\n * ```\n */\nexport function getAccessToken(request: Request, cookies?: CookieReader): string | null {\n return getBearerTokenFromRequest(request) ?? cookies?.get(authState.cookieName) ?? null\n}\n\n// ─── 服务端认证 Cookie 管理 ───\n\n/**\n * 写入 Access Token Cookie(内部使用,由 login 自动调用)。\n */\nfunction setToken(cookies: CookieWriter, token: string, maxAge?: number): void {\n cookies.set(authState.cookieName, token, {\n path: '/',\n httpOnly: true,\n sameSite: 'lax',\n // 测试开关仅放宽共享状态要求,不降低生产凭证 Cookie 的传输安全。\n secure: process.env.NODE_ENV === 'production' || process.env.HAI_CORE_ENV === 'production',\n maxAge,\n })\n}\n\n/**\n * 清除 Access Token Cookie(内部使用,由 logout 自动调用)。\n */\nfunction clearToken(cookies: CookieWriter): void {\n cookies.delete(authState.cookieName, { path: '/' })\n}\n\n// ─── 服务端高级认证 API(通过 kit.auth 暴露) ───\n\n/**\n * 获取已注入的认证操作;未配置时返回 null。\n *\n * 公共 API 不应 throw,因此此处用 null 信号配合上层短路返回 HaiResult。\n */\nfunction getAuthOperations(): AuthOperations | null {\n if (!authState.operations)\n return null\n return typeof authState.operations === 'function'\n ? authState.operations()\n : authState.operations\n}\n\n/**\n * 构造 \"auth 未配置\" 的标准 HaiResult 错误。\n *\n * 用于公共认证 API 在 `authState.operations` 未注入时返回,\n * 避免向调用方抛出未捕获异常。\n */\nfunction authNotConfiguredError(): HaiResult<never> {\n return err({\n code: 'KIT_AUTH_NOT_CONFIGURED',\n message: kitM('kit_authNotConfigured'),\n })\n}\n\n/**\n * 执行已配置的认证登录操作,并在成功时写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - 认证凭证\n * @param run - 具体 IAM 认证操作\n */\nasync function runAuthLogin<TCredentials>(\n cookies: CookieWriter,\n credentials: TCredentials,\n run: (ops: AuthOperations, credentials: TCredentials) => Promise<HaiResult<AuthResult>>,\n): Promise<HaiResult<AuthResult>> {\n const ops = getAuthOperations()\n if (!ops)\n return authNotConfiguredError()\n\n const result = await run(ops, credentials)\n if (result.success) {\n setToken(cookies, result.data.tokens.accessToken, result.data.tokens.expiresIn)\n }\n return result\n}\n\n/**\n * 服务端登录(密码):内部调用 iam.auth.login,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - 密码凭证(identifier + password)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n * @example 模块内部上下文\n * ```ts\n * async function signIn(cookies: CookieWriter, credentials: PasswordCredentials) {\n * const result = await login(cookies, credentials)\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * ```\n */\nexport async function login(\n cookies: CookieWriter,\n credentials: PasswordCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.login(input))\n}\n\n/**\n * 服务端登录(OTP 验证码):内部调用 iam.auth.loginWithOtp,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - OTP 凭证(identifier + code)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n * @example 模块内部上下文\n * ```ts\n * async function signIn(cookies: CookieWriter, credentials: OtpCredentials) {\n * const result = await loginWithOtp(cookies, credentials)\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * ```\n */\nexport async function loginWithOtp(\n cookies: CookieWriter,\n credentials: OtpCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithOtp(input))\n}\n\n/**\n * 服务端登录(LDAP):内部调用 iam.auth.loginWithLdap,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - LDAP 凭证(username + password)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n * @example 模块内部上下文\n * ```ts\n * async function signIn(cookies: CookieWriter, credentials: LdapCredentials) {\n * const result = await loginWithLdap(cookies, credentials)\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * ```\n */\nexport async function loginWithLdap(\n cookies: CookieWriter,\n credentials: LdapCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithLdap(input))\n}\n\n/**\n * 服务端登录(API Key):内部调用 iam.auth.loginWithApiKey,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - API Key 凭证(key)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n * @example 模块内部上下文\n * ```ts\n * async function signIn(cookies: CookieWriter, credentials: ApiKeyCredentials) {\n * const result = await loginWithApiKey(cookies, credentials)\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * ```\n */\nexport async function loginWithApiKey(\n cookies: CookieWriter,\n credentials: ApiKeyCredentials,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithApiKey(input))\n}\n\n/**\n * 服务端注册并登录:内部调用 iam.auth.registerAndLogin,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param options - 注册选项(username、password、email 等)\n * @returns 认证结果(成功时 Cookie 已自动写入)\n * @example 模块内部上下文\n * ```ts\n * async function signIn(cookies: CookieWriter, credentials: RegisterOptions) {\n * const result = await registerAndLogin(cookies, credentials)\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * ```\n */\nexport async function registerAndLogin(\n cookies: CookieWriter,\n options: RegisterOptions,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, options, (ops, input) => ops.registerAndLogin(input))\n}\n\n/**\n * 服务端登出:委托 iam.auth.logout 原子撤销凭据,并始终清除当前请求的 Token Cookie。\n * Cookie 清理只代表本地退出;服务端撤销失败仍返回失败,不能由调用端改成成功响应。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param accessToken - 访问令牌(为 null/undefined 时仅清除 Cookie)\n * @returns 服务端撤销结果;无令牌时仅清理 Cookie 并成功,有令牌但未配置认证操作时失败。\n * @remarks 底层控制流异常仍以 Promise 拒绝传播;Cookie 清理不吞掉业务失败。\n * @example 模块内部上下文\n * ```ts\n * async function signOut(cookies: CookieWriter, accessToken: string) {\n * const result = await logout(cookies, accessToken)\n * if (!result.success) return result.error.code\n * return result.data\n * }\n * ```\n */\nexport async function logout(\n cookies: CookieWriter,\n accessToken?: string | null,\n): Promise<HaiResult<void>> {\n try {\n if (!accessToken)\n return ok(undefined)\n const ops = getAuthOperations()\n if (!ops)\n return authNotConfiguredError()\n return await ops.logout(accessToken)\n }\n finally {\n // 本地凭据无论是否撤销确认都要移除,但失败结果仍交给调用者明确提示。\n clearToken(cookies)\n }\n}\n\n// ─── 浏览器端 Token 存储(通过 kit.auth 暴露) ───\n\n/**\n * 写入默认浏览器端 Access Token(仅页面内存,不持久化到 localStorage)。\n * @param token - 当前页面内存中使用的 access token,不写入 Web Storage。\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * kit.auth.setBrowserToken('example-only-token')\n * // 页面注销时同步清除浏览器内存令牌。\n * kit.auth.clearBrowserToken()\n * ```\n */\nexport function setBrowserToken(token: string): void {\n defaultBrowserTokenStore.set(token)\n}\n\n/**\n * 清除默认浏览器端 Access Token(仅页面内存)。\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * kit.auth.clearBrowserToken()\n * ```\n */\nexport function clearBrowserToken(): void {\n defaultBrowserTokenStore.clear()\n}\n","/**\n * @h-ai/kit — 统一客户端\n *\n * 将 CSRF Token 附加与可选传输加密封装为统一 `apiFetch`。\n * 应用层只需调用一次 `createKitClient()`,后续 API 调用即可复用同一套安全能力。\n * @module kit-client\n */\n\nimport type { CryptoFunctions, TransportClient } from '@h-ai/crypto'\nimport type { BrowserTokenStore } from '../kit-auth.js'\nimport type { KitConfig } from '../kit-config.js'\nimport { TRANSPORT_PROTOCOL } from '@h-ai/crypto'\nimport { getDefaultBrowserTokenStore } from '../kit-auth.js'\nimport { DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, resolveTransportPath, shouldUseTransportForUrl } from '../modules/crypto/kit-transport-paths.js'\n\nconst DEFAULT_KEY_EXCHANGE_URL = `/api${TRANSPORT_PROTOCOL.DEFAULT_KEY_EXCHANGE_PATH}`\n\n// ─── 类型 ───\n\n/**\n * 传输加密客户端配置\n * @example\n * ```ts\n * import type { ClientTransportConfig } from '@h-ai/kit'\n * import { crypto } from '@h-ai/crypto'\n *\n * function configAfterInit(): ClientTransportConfig { return { crypto, excludePaths: ['/api/health'] } }\n * ```\n */\nexport interface ClientTransportConfig {\n /** @h-ai/crypto 服务实例 */\n crypto: CryptoFunctions\n /** 密钥交换端点 URL(默认 `'/api/_hai/key-exchange'`) */\n keyExchangeUrl?: string\n /** 排除路径(命中后强制走明文),需与服务端 transport.excludePaths 保持一致 */\n excludePaths?: string[]\n}\n\n/**\n * Kit 客户端配置\n * @example\n * ```ts\n * import type { KitClientConfig } from '@h-ai/kit'\n *\n * const value: KitClientConfig = { auth: true }\n * void value\n * ```\n */\nexport interface KitClientConfig {\n /**\n * 传输加密配置。\n * - 不提供 / undefined:不启用传输加密,仅 CSRF\n * - 提供对象:启用传输加密\n */\n transport?: ClientTransportConfig\n /** CSRF Cookie 名称(默认 `'hai_csrf'`) */\n csrfCookieName?: string\n /** CSRF Header 名称(默认 `'X-CSRF-Token'`) */\n csrfHeaderName?: string\n /**\n * 认证配置:自动从浏览器存储读取 Access Token 并注入请求头。\n * - `true`:使用默认页面内存存储(由 `kit.auth.setBrowserToken()` 写入,不读写 localStorage)\n * - `BrowserTokenStore`:使用自定义存储\n * - 不提供 / `false`:不自动注入\n */\n auth?: boolean | BrowserTokenStore\n}\n\n/**\n * Kit 客户端实例\n * @example\n * ```ts\n * import type { KitClient } from '@h-ai/kit'\n *\n * async function read(client: KitClient) {\n * const response = await client.apiFetch('/api/health')\n * if (!response.ok) throw new Error('Request failed')\n * return response.json()\n * }\n * ```\n */\nexport interface KitClient {\n /** 统一 API fetch(写请求自动附加 CSRF,启用 transport 时自动加解密) */\n apiFetch: (url: string, init?: RequestInit) => Promise<Response>\n /** 传输加密是否就绪(未启用传输加密时始终为 true) */\n readonly ready: boolean\n /**\n * 手动触发密钥交换(transport 启用时)。\n *\n * 注意:本方法是 Client 形态的合规例外,密钥协商失败时 `throw`(与 `@h-ai/api-client` 一致),\n * 调用方需自行 `try/catch`,不返回 `HaiResult`。\n */\n init: () => Promise<void>\n /** 销毁密钥状态 */\n destroy: () => void\n}\n\n// ─── 工具函数 ───\n\n/**\n * 从 document.cookie 中读取指定名称的 Cookie 值\n *\n * @param name - Cookie 名称\n * @returns Cookie 值;不存在或运行在服务端时返回 undefined\n */\nfunction getCookie(name: string): string | undefined {\n if (typeof document === 'undefined')\n return undefined\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match?.[1]\n}\n\n/**\n * 判断当前请求体是否可以作为文本载荷进行传输加密。\n *\n * 传输加密中间件会把解密后的明文重新注入 `Request`,当前仅能可靠保留\n * JSON 字符串请求体。`FormData` / `Blob` / `ArrayBuffer` 等二进制或浏览器\n * 自动生成边界的请求体必须保持原样发送,否则文件上传会被序列化成 `{}`。\n *\n * @param body fetch 请求体\n * @returns 是否支持客户端传输加密\n */\nfunction canEncryptRequestBody(body: BodyInit | null | undefined): boolean {\n return body === undefined || body === null || typeof body === 'string'\n}\n\n/**\n * 判断全局 fetch 包装路径下,某个 `Request` 是否可走传输加密。\n *\n * 与上方 {@link canEncryptRequestBody} 区别:本谓词服务于 {@link installBrowserTransportFetch}\n * 的全局包装路径,输入是完整 `Request`,故可基于 method(GET/HEAD 无 body 安全放行)与\n * `Content-Type`(仅 JSON 可重注入明文)综合判定;而 `canEncryptRequestBody` 服务于\n * `createKitClient` 的 `apiFetch` 路径,仅拿到 `BodyInit`,只能按 body 类型粗判。\n * 两者判定维度不同是有意为之,请勿合并。\n */\nfunction canUseTransportForRequest(request: Request): boolean {\n const method = request.method.toUpperCase()\n if (method === 'GET' || method === 'HEAD') {\n return true\n }\n\n const headers = new Headers(request.headers)\n const contentType = headers.get('Content-Type') ?? ''\n if (contentType.includes('application/json')) {\n return true\n }\n\n return request.body === null\n}\n\nfunction getCurrentOrigin(): string {\n if (typeof window !== 'undefined' && window.location?.origin) {\n return window.location.origin\n }\n\n return 'http://localhost'\n}\n\nfunction toRequest(input: RequestInfo | URL, init?: RequestInit): Request {\n if (input instanceof Request)\n return init ? new Request(input, init) : input\n\n return new Request(input, init)\n}\n\n/**\n * 浏览器全局 fetch 包装的幂等安装状态;属于当前浏览器页面。\n * @example 模块内部上下文\n * ```ts\n * const state: BrowserTransportFetchState = globalThis\n * const installed = state.__haiKitTransportFetchInstalled ?? false\n * void installed\n * ```\n */\ntype BrowserTransportFetchState = typeof globalThis & {\n __haiKitOriginalFetch?: typeof fetch\n __haiKitTransportFetchInstalled?: boolean\n}\n\n/**\n * 在浏览器启动时安装同源 transport fetch 包装。\n *\n * 该包装作用于同源 `/api/*` 与 SvelteKit `__data.json` 请求,页面文档、\n * 静态资源以及 multipart/form-data 等上传请求保持原样。应用只需安装一次,\n * 后续 `fetch` / SvelteKit 内部 data fetch / `kit.client.create()` 明文发送路径\n * 都会共用这层传输加密能力。\n * @param transportConfig - 已初始化 crypto、同源密钥交换路径及排除路径;省略时不安装包装。\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * // 不传配置时不安装全局包装;启用时应传已初始化 crypto 与同源端点。\n * kit.client.installBrowserTransportFetch()\n * ```\n */\nexport function installBrowserTransportFetch(transportConfig?: ClientTransportConfig): void {\n if (typeof window === 'undefined' || !transportConfig)\n return\n\n const fetchState = globalThis as BrowserTransportFetchState\n if (fetchState.__haiKitTransportFetchInstalled)\n return\n\n const originalFetch = fetchState.__haiKitOriginalFetch ?? globalThis.fetch.bind(globalThis)\n fetchState.__haiKitOriginalFetch = originalFetch\n\n const origin = getCurrentOrigin()\n const keyExchangeUrl = new URL(transportConfig.keyExchangeUrl ?? DEFAULT_KEY_EXCHANGE_URL, origin).toString()\n const keyExchangePath = resolveTransportPath(keyExchangeUrl, origin)\n const transportClient = transportConfig.crypto.transport.createClient({\n keyExchangeUrl,\n fetch: originalFetch,\n })\n\n const wrappedFetch: typeof fetch = async (input, init) => {\n const request = toRequest(input, init)\n const requestUrl = new URL(request.url, origin)\n const headers = new Headers(request.headers)\n\n if (!shouldUseTransportForUrl(requestUrl, {\n origin,\n keyExchangePath,\n excludePaths: transportConfig.excludePaths,\n })) {\n return originalFetch(input, init)\n }\n\n if (headers.has(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER))\n return originalFetch(input, init)\n\n if (!canUseTransportForRequest(request))\n return originalFetch(input, init)\n\n return transportClient.encryptedFetch(request)\n }\n\n globalThis.fetch = wrappedFetch\n window.fetch = wrappedFetch\n fetchState.__haiKitTransportFetchInstalled = true\n}\n\n/**\n * 用 `config/_kit.yml` 解析后的 {@link KitConfig} 一次性安装浏览器端传输加密。\n *\n * 行为:\n * - `config.transport === false` 时直接返回(不启用传输加密);\n * - 否则启动 {@link installBrowserTransportFetch},并预热 {@link CryptoFunctions} 模块。\n *\n * 应用层(SvelteKit `+layout.svelte` 或浏览器入口)只需调用此函数一次,\n * 之后所有同源 `/api/*` 与 `__data.json` 请求都会自动走传输加密。\n *\n * @param config 已解析的 kit 顶层配置\n * @param deps 外部依赖\n * @param deps.crypto `@h-ai/crypto` 模块实例\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * import { crypto } from '@h-ai/crypto'\n *\n * function installAfterCryptoInit() {\n * kit.client.installBrowserTransport({ server: { host: '127.0.0.1', port: 3000 }, transport: { keyExchangePath: '/api/_hai/key-exchange', excludePaths: ['/api/health'] } }, { crypto })\n * }\n * ```\n */\nexport function installBrowserTransport(\n config: KitConfig,\n deps: { crypto: CryptoFunctions },\n): void {\n if (typeof window === 'undefined' || config.transport === false)\n return\n\n // crypto.init() 当前为同步副作用(signature 是 async 仅为接口一致性),\n // 此处不 await,以便 `+layout.svelte` 顶层模块加载阶段就完成初始化。\n void deps.crypto.init()\n\n installBrowserTransportFetch({\n crypto: deps.crypto,\n keyExchangeUrl: config.transport.keyExchangePath,\n excludePaths: [...config.transport.excludePaths],\n })\n}\n\n// ─── 主函数 ───\n\n/**\n * 创建 Kit 客户端\n *\n * 返回统一的 `apiFetch`,内部自动完成:\n * 1. CSRF Token 附加(写方法自动读取 Cookie + 设置 Header)\n * 2. 密钥交换(首次写请求时 lazy init,transport 启用时)\n * 3. 请求体加密(transport 启用时)\n * 4. 响应体解密(transport 启用时)\n *\n * @param config - 客户端配置\n * @returns KitClient 实例\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * const client = kit.client.create({ auth: true })\n * try {\n * await client.init() // Client API 使用 Promise 拒绝;不是 HaiResult。\n * const response = await client.apiFetch('/api/health')\n * if (!response.ok) throw new Error('Request failed')\n * } finally {\n * client.destroy()\n * }\n * ```\n */\nexport function createKitClient(config: KitClientConfig = {}): KitClient {\n const {\n transport: transportConfig,\n csrfCookieName = 'hai_csrf',\n csrfHeaderName = 'X-CSRF-Token',\n auth: authConfig,\n } = config\n\n // ── Auth token store ──\n // 安全默认:auth:true 只读取页面内存 token,避免隐式 localStorage 持久化敏感凭证。\n const authStore: BrowserTokenStore | null = authConfig\n ? (authConfig === true ? getDefaultBrowserTokenStore() : authConfig)\n : null\n\n // ── 传输加密状态 ──\n const transportOrigin = getCurrentOrigin()\n const transportKeyExchangeUrl = transportConfig?.keyExchangeUrl ?? DEFAULT_KEY_EXCHANGE_URL\n const transportKeyExchangePath = transportConfig\n ? resolveTransportPath(transportKeyExchangeUrl, transportOrigin)\n : DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH\n const transportClient: TransportClient | null = transportConfig\n ? transportConfig.crypto.transport.createClient({\n keyExchangeUrl: transportKeyExchangeUrl,\n fetch,\n })\n : null\n\n /**\n * 统一 API fetch\n */\n async function apiFetch(url: string, init: RequestInit = {}): Promise<Response> {\n const method = (init.method ?? 'GET').toUpperCase()\n const isWriteMethod = !['GET', 'HEAD', 'OPTIONS'].includes(method)\n const canUseTransport = canEncryptRequestBody(init.body)\n\n const headers = new Headers(init.headers)\n\n // ── Auth ──\n if (authStore) {\n const token = authStore.get()\n if (token) {\n headers.set('Authorization', `Bearer ${token}`)\n }\n }\n\n // ── CSRF ──\n if (isWriteMethod) {\n const csrfToken = getCookie(csrfCookieName)\n if (csrfToken) {\n headers.set(csrfHeaderName, csrfToken)\n }\n }\n\n // ── 传输加密 ──\n if (\n transportClient\n && canUseTransport\n && shouldUseTransportForUrl(url, {\n origin: transportOrigin,\n keyExchangePath: transportKeyExchangePath,\n excludePaths: transportConfig?.excludePaths,\n })\n ) {\n return transportClient.encryptedFetch(url, { ...init, headers })\n }\n\n // ── 发送(FormData/Blob/Stream 等保持原样,不破坏浏览器边界) ──\n return fetch(url, { ...init, headers })\n }\n\n return {\n apiFetch,\n get ready() { return !transportClient || transportClient.ready() },\n async init() {\n if (!transportClient)\n return\n const result = await transportClient.init()\n if (!result.success)\n throw new Error(result.error.message)\n },\n destroy() {\n transportClient?.destroy()\n },\n }\n}\n","/** 与标准 Kit 认证端点配套的表单动作;复用已有 CSRF/transport fetch。 */\nimport type { IamLoginInput, IamPublicConfig, IamRegisterInput } from '@h-ai/api-contract'\nimport type { HaiResult } from '@h-ai/core'\nimport type { KitClient } from './kit-client.js'\nimport { apiContract, IamPublicConfigSchema } from '@h-ai/api-contract'\nimport { ok } from '@h-ai/core'\nimport { z } from 'zod'\nimport { createKitClient } from './kit-client.js'\n\n/**\n * 创建标准 Cookie 认证表单动作;复用可选 apiFetch 以保留 CSRF 和传输配置。\n * @param options - 可复用的 apiFetch 和标准认证端点 basePath。\n * @param options.apiFetch - 已装配 CSRF/transport 的 fetch;省略时使用默认 Kit 客户端。\n * @param options.basePath - 同源认证端点前缀,默认 /api/auth。\n * @returns 标准表单动作;业务返回 HaiResult,网络/格式错误会拒绝 Promise。\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * const actions = kit.client.createAuthActions({ basePath: '/api/auth' })\n * void actions\n * ```\n */\nexport function createKitAuthActions(options: { apiFetch?: KitClient['apiFetch'], basePath?: string } = {}) {\n const apiFetch = options.apiFetch ?? createKitClient().apiFetch\n const base = options.basePath ?? '/api/auth'\n async function post(path: string, input: object): Promise<HaiResult<unknown>> {\n const response = await apiFetch(`${base}/${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(input) })\n // void 成功响应在 JSON 中省略 data,统一还原为 HaiResult 的 data: undefined。\n const result = apiContract.haiResultSchema(z.unknown().optional()).parse(await response.json())\n return result.success ? ok(result.data) : result\n }\n return {\n async publicConfig(): Promise<HaiResult<IamPublicConfig | null>> {\n const response = await apiFetch(`${base}/config`)\n return apiContract.haiResultSchema(IamPublicConfigSchema.nullable()).parse(await response.json())\n },\n login(input: IamLoginInput & { method?: 'password' | 'ldap' | 'email' | 'sms', code?: string }) {\n if (input.method === 'ldap')\n return post('login/ldap', { username: input.identifier, password: input.password })\n if (input.method === 'email' || input.method === 'sms')\n return post('login/otp', { identifier: input.identifier, code: input.code })\n return post('login', { identifier: input.identifier, password: input.password })\n },\n register: (input: IamRegisterInput) => post('register', { ...input, confirmPassword: input.password }),\n sendOtp: (identifier: string) => post('otp/send', { identifier }),\n requestPasswordReset: (identifier: string) => post('password-reset/request', { identifier }),\n confirmPasswordReset: (token: string, newPassword: string) => post('password-reset/confirm', { token, newPassword }),\n }\n}\n"]}
@@ -1,8 +1,7 @@
1
- import { DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, shouldHandleTransportPath, shouldExcludeTransportPath } from './chunk-NVYIUBU7.js';
2
- import { kitM } from './chunk-QHYNL24G.js';
3
- import { TRANSPORT_PROTOCOL } from '@h-ai/crypto';
1
+ import { DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, shouldHandleTransportPath, shouldExcludeTransportPath } from './chunk-XC7I2L3G.js';
2
+ import { kitM } from './chunk-OVRZIDCD.js';
3
+ import { TRANSPORT_PROTOCOL, authenticateTransportRequest, readTransportBody, TRANSPORT_BODY_LIMITS, isValidSM2PublicKey } from '@h-ai/crypto';
4
4
 
5
- var MAX_ENCRYPTED_BODY = 1048576;
6
5
  var ENCRYPTABLE_RESPONSE_CONTENT_TYPES = ["application/json", "text/sveltekit-data"];
7
6
  function transportEncryptionMiddleware(config) {
8
7
  if (!config.enabled) {
@@ -10,11 +9,9 @@ function transportEncryptionMiddleware(config) {
10
9
  }
11
10
  const keyExchangePath = config.keyExchangePath ?? DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH;
12
11
  const excludePaths = config.excludePaths ?? [];
13
- const encryptResponse = config.encryptResponse ?? true;
14
- const requireEncryption = config.requireEncryption ?? true;
15
12
  const result = config.crypto.transport.createServer({
16
- keyStore: config.keyStore,
17
- maxClients: config.maxClients
13
+ serverKeyRing: config.serverKeyRing,
14
+ keyStore: config.keyStore
18
15
  });
19
16
  if (!result.success) {
20
17
  return createUnavailableTransportMiddleware(keyExchangePath, excludePaths);
@@ -33,50 +30,48 @@ function transportEncryptionMiddleware(config) {
33
30
  return next();
34
31
  }
35
32
  const clientId = event.request.headers.get(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER);
36
- if (!clientId) {
37
- if (requireEncryption) {
38
- return jsonError(kitM("kit_transportClientIdRequired"), 400);
39
- }
40
- return next();
41
- }
42
- const clientPublicKey = await manager.getClientPublicKey(clientId);
43
- if (!clientPublicKey) {
44
- return jsonError(kitM("kit_transportClientKeyNotFound"), 400);
45
- }
46
- if (hasBody(event.request.method) && !isRequestBodyEmpty(event.request)) {
47
- const decryptResult = await decryptRequestBody(event, manager, requireEncryption);
48
- if (decryptResult) {
49
- return decryptResult;
50
- }
33
+ if (!clientId)
34
+ return jsonError(kitM("kit_transportClientIdRequired"), 400);
35
+ let requestContext;
36
+ try {
37
+ const verified = await authenticateTransportRequest(manager, event.request);
38
+ if (!verified.success)
39
+ return jsonError(kitM("kit_transportDecryptFailed"), 400);
40
+ requestContext = verified.data.context;
41
+ if (event.request.method !== "GET" && event.request.method !== "HEAD")
42
+ replaceRequestBody(event, verified.data.plaintext);
43
+ } catch (error) {
44
+ return jsonError(kitM("kit_transportInvalidPayload"), error instanceof RangeError ? 413 : 400);
51
45
  }
52
46
  const response = await next();
53
- if (!encryptResponse) {
54
- return response;
55
- }
56
- if (isEmptyResponse(response)) {
57
- return response;
58
- }
47
+ const bodyless = event.request.method === "HEAD" || isEmptyResponse(response);
48
+ if (bodyless)
49
+ void response.body?.cancel().catch(() => {
50
+ });
59
51
  const contentType = response.headers.get("Content-Type") ?? "";
60
- if (!canEncryptResponseContentType(contentType)) {
61
- return jsonError(kitM("kit_transportEncryptFailed"), 500);
62
- }
63
- const contentLength = response.headers.get("Content-Length");
64
- if (contentLength && Number.parseInt(contentLength, 10) > MAX_ENCRYPTED_BODY) {
52
+ if (!bodyless && !canEncryptResponseContentType(contentType)) {
53
+ void response.body?.cancel().catch(() => {
54
+ });
65
55
  return jsonError(kitM("kit_transportEncryptFailed"), 500);
66
56
  }
67
57
  try {
68
- const responseBody = await response.clone().text();
69
- if (!responseBody) {
70
- return response;
71
- }
72
- const encryptedPayload = await manager.encryptResponse(clientId, responseBody);
58
+ const responseBody = bodyless ? "" : await readTransportBody(response.body, {
59
+ maxBytes: TRANSPORT_BODY_LIMITS.plaintextBytes,
60
+ signal: event.request.signal
61
+ });
62
+ const headers = new Headers(response.headers);
63
+ headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
64
+ headers.delete(TRANSPORT_PROTOCOL.ENVELOPE_HEADER);
65
+ headers.delete("Content-Length");
66
+ headers.delete("Content-Encoding");
67
+ const encryptedPayload = await manager.encryptResponse(requestContext, responseBody, response.status);
73
68
  if (!encryptedPayload.success) {
74
69
  return jsonError(kitM("kit_transportEncryptFailed"), 500);
75
70
  }
76
- const headers = new Headers(response.headers);
77
71
  headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE);
78
- headers.delete("Content-Length");
79
- return new Response(JSON.stringify(encryptedPayload.data), {
72
+ if (bodyless)
73
+ headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encryptedPayload.data));
74
+ return new Response(bodyless ? null : JSON.stringify(encryptedPayload.data), {
80
75
  status: response.status,
81
76
  statusText: response.statusText,
82
77
  headers
@@ -107,7 +102,7 @@ function createUnavailableTransportMiddleware(keyExchangePath, excludePaths) {
107
102
  function isEmptyResponse(response) {
108
103
  if ([204, 304].includes(response.status))
109
104
  return true;
110
- return response.headers.get("Content-Length") === "0";
105
+ return response.body === null;
111
106
  }
112
107
  function getRequestPathname(request) {
113
108
  return new URL(request.url).pathname;
@@ -119,41 +114,42 @@ function jsonError(message, status) {
119
114
  });
120
115
  }
121
116
  async function handleKeyExchange(manager, request) {
117
+ let body;
118
+ try {
119
+ body = JSON.parse(await readTransportBody(request.body, {
120
+ maxBytes: TRANSPORT_BODY_LIMITS.keyExchangeBytes,
121
+ signal: request.signal
122
+ }));
123
+ } catch (error) {
124
+ return jsonError(kitM("kit_transportInvalidPayload"), error instanceof RangeError ? 413 : 400);
125
+ }
126
+ if (!body || typeof body !== "object" || !("clientPublicKey" in body) || !isValidSM2PublicKey(body.clientPublicKey) || new TextEncoder().encode(body.clientPublicKey).byteLength > TRANSPORT_BODY_LIMITS.publicKeyBytes) {
127
+ return jsonError(kitM("kit_transportInvalidPayload"), 400);
128
+ }
122
129
  try {
123
- const body = await request.json();
124
- if (!body.clientPublicKey || typeof body.clientPublicKey !== "string") {
125
- return jsonError(kitM("kit_transportInvalidPayload"), 400);
126
- }
127
- const clientId = await manager.registerClientKey(body.clientPublicKey);
128
130
  const serverPublicKey = manager.getServerPublicKey();
131
+ if (new TextEncoder().encode(serverPublicKey).byteLength > TRANSPORT_BODY_LIMITS.publicKeyBytes)
132
+ return jsonError(kitM("kit_transportKeyExchangeFailed"), 500);
133
+ const clientId = await manager.registerClientKey(body.clientPublicKey);
129
134
  return new Response(
130
- JSON.stringify({ serverPublicKey, clientId }),
135
+ JSON.stringify({ serverKeyId: manager.getServerKeyId(), serverPublicKey, clientId }),
131
136
  { status: 200, headers: { "Content-Type": "application/json" } }
132
137
  );
133
138
  } catch {
134
139
  return jsonError(kitM("kit_transportKeyExchangeFailed"), 500);
135
140
  }
136
141
  }
137
- function hasBody(method) {
138
- return ["POST", "PUT", "PATCH", "DELETE"].includes(method.toUpperCase());
139
- }
140
- function isRequestBodyEmpty(request) {
141
- if (request.body === null)
142
- return true;
143
- const contentLength = request.headers.get("Content-Length");
144
- if (contentLength !== null)
145
- return Number.parseInt(contentLength, 10) === 0;
146
- return false;
147
- }
148
142
  function replaceRequestBody(event, body) {
149
143
  const headers = new Headers(event.request.headers);
150
144
  headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
151
145
  headers.delete("Content-Length");
146
+ headers.delete("Content-Encoding");
152
147
  headers.set("Content-Type", "application/json; charset=utf-8");
153
148
  const newRequest = new Request(event.request.url, {
154
149
  method: event.request.method,
155
150
  headers,
156
- body
151
+ body,
152
+ signal: event.request.signal
157
153
  });
158
154
  Object.defineProperty(event, "request", {
159
155
  value: newRequest,
@@ -161,55 +157,7 @@ function replaceRequestBody(event, body) {
161
157
  configurable: true
162
158
  });
163
159
  }
164
- async function decryptRequestBody(event, manager, requireEncryption) {
165
- const isEncrypted = event.request.headers.get(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER) === TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE;
166
- if (!isEncrypted) {
167
- if (!requireEncryption)
168
- return null;
169
- return jsonError(kitM("kit_transportInvalidPayload"), 400);
170
- }
171
- const contentType = event.request.headers.get("Content-Type") ?? "";
172
- if (!contentType.includes("application/json")) {
173
- if (!requireEncryption) {
174
- return null;
175
- }
176
- return jsonError(kitM("kit_transportInvalidPayload"), 400);
177
- }
178
- const bodyText = await event.request.text();
179
- if (!bodyText) {
180
- return null;
181
- }
182
- let payload;
183
- try {
184
- payload = JSON.parse(bodyText);
185
- } catch {
186
- if (!requireEncryption) {
187
- replaceRequestBody(event, bodyText);
188
- return null;
189
- }
190
- return jsonError(kitM("kit_transportDecryptFailed"), 400);
191
- }
192
- if (!isEncryptedPayloadShape(payload)) {
193
- if (!requireEncryption) {
194
- replaceRequestBody(event, bodyText);
195
- return null;
196
- }
197
- return jsonError(kitM("kit_transportInvalidPayload"), 400);
198
- }
199
- const plaintext = manager.decryptRequest(payload);
200
- if (!plaintext.success) {
201
- return jsonError(kitM("kit_transportDecryptFailed"), 400);
202
- }
203
- replaceRequestBody(event, plaintext.data);
204
- return null;
205
- }
206
- function isEncryptedPayloadShape(payload) {
207
- if (!payload || typeof payload !== "object")
208
- return false;
209
- const p = payload;
210
- return typeof p.encryptedKey === "string" && typeof p.ciphertext === "string" && typeof p.iv === "string";
211
- }
212
160
 
213
161
  export { transportEncryptionMiddleware };
214
- //# sourceMappingURL=chunk-MLSP67JF.js.map
215
- //# sourceMappingURL=chunk-MLSP67JF.js.map
162
+ //# sourceMappingURL=chunk-333FGMGQ.js.map
163
+ //# sourceMappingURL=chunk-333FGMGQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/modules/crypto/kit-transport-middleware.ts"],"names":[],"mappings":"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","file":"chunk-333FGMGQ.js","sourcesContent":["/**\n * @h-ai/kit — 传输加密中间件\n *\n * SvelteKit 中间件,自动对请求/响应进行传输加密解密。\n * @module kit-transport-middleware\n */\n\nimport type { TransportEncryptionManager, TransportMessageContext } from '@h-ai/crypto'\nimport type { Middleware } from '../../kit-types.js'\nimport type { TransportEncryptionConfig } from './kit-crypto-types.js'\nimport { authenticateTransportRequest, isValidSM2PublicKey, readTransportBody, TRANSPORT_BODY_LIMITS, TRANSPORT_PROTOCOL } from '@h-ai/crypto'\nimport { kitM } from '../../kit-i18n.js'\nimport { DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, shouldExcludeTransportPath, shouldHandleTransportPath } from './kit-transport-paths.js'\n\nconst ENCRYPTABLE_RESPONSE_CONTENT_TYPES = ['application/json', 'text/sveltekit-data']\n\n/**\n * 创建传输加密中间件\n *\n * @param config - 传输加密配置\n * @returns SvelteKit 中间件\n * @example 模块内部上下文\n * ```ts\n * function install(config: TransportEncryptionConfig) {\n * return transportEncryptionMiddleware(config) // 受管密钥环、共享 keyStore 与 crypto 生命周期由应用管理。\n * }\n * ```\n */\nexport function transportEncryptionMiddleware(config: TransportEncryptionConfig): Middleware {\n // 未启用时直接透传\n if (!config.enabled) {\n return async (_context, next) => next()\n }\n\n const keyExchangePath = config.keyExchangePath ?? DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH\n const excludePaths = config.excludePaths ?? []\n\n // 初始化传输加密管理器\n const result = config.crypto.transport.createServer({\n serverKeyRing: config.serverKeyRing,\n keyStore: config.keyStore,\n })\n if (!result.success) {\n // 安全策略:transport 已启用但服务端管理器不可用时,受保护路由必须 fail-closed,禁止明文透传。\n return createUnavailableTransportMiddleware(keyExchangePath, excludePaths)\n }\n const manager = result.data\n\n return async (context, next) => {\n const { event } = context\n const pathname = getRequestPathname(event.request)\n\n // 密钥交换端点:委托给 keyExchangeHandler\n if (pathname === keyExchangePath && event.request.method === 'POST') {\n return handleKeyExchange(manager, event.request)\n }\n\n // 仅对同源 API endpoint 与 SvelteKit __data 请求启用 transport;\n // 页面文档、静态资源等仍透传,避免首页 HTML 被误拦截。\n if (!shouldHandleTransportPath(pathname, keyExchangePath)) {\n return next()\n }\n\n // 排除路径不做加解密\n if (shouldExcludeTransportPath(pathname, excludePaths, keyExchangePath)) {\n return next()\n }\n\n // 请求头中无 X-Client-Id 时的处理\n const clientId = event.request.headers.get(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER)\n if (!clientId)\n return jsonError(kitM('kit_transportClientIdRequired'), 400)\n\n // ── 解密请求 ──\n // 不依赖可伪造/省略的 Content-Length/Type 判空,非 null 流必须进入实际内容检查。\n let requestContext: TransportMessageContext\n try {\n const verified = await authenticateTransportRequest(manager, event.request)\n if (!verified.success)\n return jsonError(kitM('kit_transportDecryptFailed'), 400)\n requestContext = verified.data.context\n if (event.request.method !== 'GET' && event.request.method !== 'HEAD')\n replaceRequestBody(event, verified.data.plaintext)\n }\n catch (error) {\n return jsonError(kitM('kit_transportInvalidPayload'), error instanceof RangeError ? 413 : 400)\n }\n\n // ── 执行后续中间件/端点 ──\n const response = await next()\n\n // ── 加密响应 ──\n\n // 只允许可安全加密的响应体离开受保护路由;无法加密时 fail-closed,禁止明文泄露。\n const bodyless = event.request.method === 'HEAD' || isEmptyResponse(response)\n if (bodyless)\n void response.body?.cancel().catch(() => {})\n\n const contentType = response.headers.get('Content-Type') ?? ''\n if (!bodyless && !canEncryptResponseContentType(contentType)) {\n void response.body?.cancel().catch(() => {})\n return jsonError(kitM('kit_transportEncryptFailed'), 500)\n }\n try {\n const responseBody = bodyless\n ? ''\n : await readTransportBody(response.body, {\n maxBytes: TRANSPORT_BODY_LIMITS.plaintextBytes,\n signal: event.request.signal,\n })\n const headers = new Headers(response.headers)\n headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER)\n headers.delete(TRANSPORT_PROTOCOL.ENVELOPE_HEADER)\n headers.delete('Content-Length')\n headers.delete('Content-Encoding')\n\n const encryptedPayload = await manager.encryptResponse(requestContext, responseBody, response.status)\n if (!encryptedPayload.success) {\n return jsonError(kitM('kit_transportEncryptFailed'), 500)\n }\n headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE)\n if (bodyless)\n headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encryptedPayload.data))\n return new Response(bodyless ? null : JSON.stringify(encryptedPayload.data), {\n status: response.status,\n statusText: response.statusText,\n headers,\n })\n }\n catch {\n return jsonError(kitM('kit_transportEncryptFailed'), 500)\n }\n }\n}\n\nfunction canEncryptResponseContentType(contentType: string): boolean {\n return ENCRYPTABLE_RESPONSE_CONTENT_TYPES.some(type => contentType.includes(type))\n}\n\nfunction createUnavailableTransportMiddleware(\n keyExchangePath: string,\n excludePaths: string[],\n): Middleware {\n return async (context, next) => {\n const pathname = getRequestPathname(context.event.request)\n if (pathname === keyExchangePath) {\n return jsonError(kitM('kit_transportKeyGenerationFailed'), 500)\n }\n if (!shouldHandleTransportPath(pathname, keyExchangePath)) {\n return next()\n }\n if (shouldExcludeTransportPath(pathname, excludePaths, keyExchangePath)) {\n return next()\n }\n return jsonError(kitM('kit_transportKeyGenerationFailed'), 500)\n }\n}\n\nfunction isEmptyResponse(response: Response): boolean {\n if ([204, 304].includes(response.status))\n return true\n // Content-Length 不能证明响应流为空,避免明文伪装零长度后绕过加密。\n return response.body === null\n}\n\nfunction getRequestPathname(request: Request): string {\n return new URL(request.url).pathname\n}\n\nfunction jsonError(message: string, status: number): Response {\n return new Response(JSON.stringify({ error: message }), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\n/**\n * 处理密钥交换 POST 请求\n *\n * 接收客户端公钥,注册并返回服务端公钥与 clientId。\n *\n * @param manager - 传输加密管理器\n * @param request - 原始 Request\n * @returns JSON Response\n */\nasync function handleKeyExchange(\n manager: TransportEncryptionManager,\n request: Request,\n): Promise<Response> {\n let body: unknown\n try {\n body = JSON.parse(await readTransportBody(request.body, {\n maxBytes: TRANSPORT_BODY_LIMITS.keyExchangeBytes,\n signal: request.signal,\n }))\n }\n catch (error) {\n return jsonError(kitM('kit_transportInvalidPayload'), error instanceof RangeError ? 413 : 400)\n }\n if (!body || typeof body !== 'object' || !('clientPublicKey' in body)\n || !isValidSM2PublicKey(body.clientPublicKey)\n || new TextEncoder().encode(body.clientPublicKey).byteLength > TRANSPORT_BODY_LIMITS.publicKeyBytes) {\n return jsonError(kitM('kit_transportInvalidPayload'), 400)\n }\n\n try {\n const serverPublicKey = manager.getServerPublicKey()\n if (new TextEncoder().encode(serverPublicKey).byteLength > TRANSPORT_BODY_LIMITS.publicKeyBytes)\n return jsonError(kitM('kit_transportKeyExchangeFailed'), 500)\n const clientId = await manager.registerClientKey(body.clientPublicKey)\n\n return new Response(\n JSON.stringify({ serverKeyId: manager.getServerKeyId(), serverPublicKey, clientId }),\n { status: 200, headers: { 'Content-Type': 'application/json' } },\n )\n }\n catch {\n return jsonError(kitM('kit_transportKeyExchangeFailed'), 500)\n }\n}\n\n/**\n * 用于替换已验证请求正文的最小可写容器。\n * @example 模块内部上下文\n * ```ts\n * const holder: RequestHolder = { request: new Request('https://app.example.com/api/articles', { method: 'POST', body: '{}' }) }\n * replaceRequestBody(holder, JSON.stringify({ title: 'Example' }))\n * ```\n */\ninterface RequestHolder {\n request: Request\n}\n\n/**\n * 重建 SvelteKit 事件中的 Request。\n *\n * 有界认证会消费密文 body;只将成功认证的明文恢复给后续端点。\n *\n * @param event SvelteKit 请求事件\n * @param body 新请求体\n */\nfunction replaceRequestBody(event: RequestHolder, body: string): void {\n const headers = new Headers(event.request.headers)\n headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER)\n headers.delete('Content-Length')\n headers.delete('Content-Encoding')\n headers.set('Content-Type', 'application/json; charset=utf-8')\n const newRequest = new Request(event.request.url, {\n method: event.request.method,\n headers,\n body,\n signal: event.request.signal,\n })\n Object.defineProperty(event, 'request', {\n value: newRequest,\n writable: true,\n configurable: true,\n })\n}\n"]}