@h-ai/kit 0.1.0-alpha.74 → 0.1.0-alpha.75

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 (33) hide show
  1. package/README.en.md +3 -1
  2. package/README.ja.md +4 -0
  3. package/README.md +4 -0
  4. package/dist/browser.d.ts +3 -3
  5. package/dist/browser.js +4 -4
  6. package/dist/{chunk-UTCS6FR7.js → chunk-726DCCEK.js} +63 -55
  7. package/dist/chunk-726DCCEK.js.map +1 -0
  8. package/dist/{chunk-OVRZIDCD.js → chunk-KRR7QGMN.js} +11 -5
  9. package/dist/chunk-KRR7QGMN.js.map +1 -0
  10. package/dist/{chunk-6UCUIS35.js → chunk-QPJ5AWXX.js} +23 -8
  11. package/dist/chunk-QPJ5AWXX.js.map +1 -0
  12. package/dist/{chunk-5O43CLW3.js → chunk-TYZ3S2AY.js} +4 -3
  13. package/dist/chunk-TYZ3S2AY.js.map +1 -0
  14. package/dist/{chunk-2PWGJCWX.js → chunk-X3NGOGT6.js} +3 -3
  15. package/dist/{chunk-2PWGJCWX.js.map → chunk-X3NGOGT6.js.map} +1 -1
  16. package/dist/client/index.d.ts +1 -1
  17. package/dist/client/index.js +2 -2
  18. package/dist/index.d.ts +6 -6
  19. package/dist/index.js +10 -7
  20. package/dist/index.js.map +1 -1
  21. package/dist/{kit-a2a-types-ChZCbX3t.d.ts → kit-a2a-types-BPPh1x_a.d.ts} +1 -1
  22. package/dist/{kit-client-auth-Dr0i5Swn.d.ts → kit-client-auth-DZOoQxlp.d.ts} +6 -0
  23. package/dist/{kit-crypto-types-Cwj9bprV.d.ts → kit-crypto-types-DgdeRenR.d.ts} +2 -0
  24. package/dist/{kit-types-B6rjOQWx.d.ts → kit-types-yogJY1i6.d.ts} +2 -0
  25. package/dist/modules/a2a/index.d.ts +3 -3
  26. package/dist/modules/a2a/index.js +2 -2
  27. package/dist/modules/crypto/index.d.ts +2 -2
  28. package/dist/modules/crypto/index.js +2 -2
  29. package/package.json +10 -10
  30. package/dist/chunk-5O43CLW3.js.map +0 -1
  31. package/dist/chunk-6UCUIS35.js.map +0 -1
  32. package/dist/chunk-OVRZIDCD.js.map +0 -1
  33. package/dist/chunk-UTCS6FR7.js.map +0 -1
package/README.en.md CHANGED
@@ -218,6 +218,8 @@ Empty POSTs still send an authenticated envelope for empty plaintext. GET/HEAD c
218
218
 
219
219
  Set `transport.maxPlaintextBytes` for both request and response JSON plaintext, measured in UTF-8 bytes: default 1 MiB, integer range 1 KiB–64 MiB. Clients learn the server limit during key exchange. `resolveTransportBodyLimits()` derives encrypted/proxy bytes as twice the plaintext limit plus 32 KiB. Handshake, public-key, header and replay constraints remain independent. Oversized streams are cancelled and readers released: requests return 413; oversized responses return an encrypted 500 capacity error, recognized as `TransportBodyTooLargeError` / `hai:crypto:070`. [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
220
220
 
221
+ Ordinary routes retain `transport.maxPlaintextBytes` (auto/omitted: 1 MiB). Declare Feedback report intake, evidence, attachments and ZIP download capacity separately using `routePlaintextBytes` and `feedback.budget.maxTransportBytes`; `*` matches one complete path segment and explicit host route values take precedence. Handshakes, adapter-node and proxies use the maximum route capacity. HTTP adapters admit at most 2 explicitly declared material requests above 1 MiB and a separate 16 ordinary business requests, releasing admission after the entire response or failure. Busy admission returns 503 / `hai:crypto:072`; business writes are not automatically replayed.
222
+
221
223
  serv/kit return an encrypted HTTP 400 time error for an out-of-window message only after request binding, signature and MAC verification. The client throws `TransportTimeError` (`hai:crypto:071`) with advice to synchronize device/server clocks and refresh. `api-client` maps it to `VALIDATION_FAILED` and preserves `cause.transportCode`. Requests are not replayed automatically and replay windows remain unchanged; tampered or unauthenticated messages retain the generic authentication failure.
222
224
 
223
225
  #### Shared transport configuration with `_kit.yml`
@@ -364,7 +366,7 @@ Set `kit.client.create({ recoverSession })` to sign in again without discarding
364
366
 
365
367
  ## Transport capacity and module budgets
366
368
 
367
- Configuration transport.maxPlaintextBytes accepts an explicit integer from 1 KiB to 64 MiB or auto. Auto/omitted values normalize to undefined: the default remains 1 MiB without module requirements, while feedback hosts combine requirements through crypto.resolveTransportBodyLimits and requiredPlaintextBytes. Insufficient explicit limits fail startup/packaging instead of being overwritten. Proxy, adapter-node, handshake and runtime budgets must agree; attachment, decompression and row limits remain independent.
369
+ For a sensitive call, supply `reauthorize: () => sensitiveActions.authorize(operation)` in the third Kit apiFetch argument or typed-client options.context. After session rotation/recovery or an explicit proof rejection, obtain a new purpose-bound proof before one retry. Cancellation sends no further write; without a callback, an old proof is never replayed.
368
370
 
369
371
  ## Development and testing
370
372
 
package/README.ja.md CHANGED
@@ -224,6 +224,8 @@ transport は既定で同一オリジンの `/api/*`、SvelteKit の `__data.jso
224
224
 
225
225
  `transport.maxPlaintextBytes` でリクエストとレスポンスの JSON 平文容量を共通設定します。単位は UTF-8 バイト、既定値は 1 MiB、有効範囲は 1 KiB~64 MiB の整数です。クライアントは鍵交換で上限を取得し、`resolveTransportBodyLimits()` が暗号文とプロキシ容量(平文の 2 倍 + 32 KiB)を算出します。鍵交換、公開鍵、認証ヘッダー、防リプレイ制約は独立です。超過時は元ストリームを停止して reader を解放します。要求は 413、応答は暗号化された 500 容量エラーとなり、`TransportBodyTooLargeError` / `hai:crypto:070` で識別できます。 [Transport capacity and upgrade](../crypto/TRANSPORT-CAPACITY.md)。
226
226
 
227
+ 通常業務は `transport.maxPlaintextBytes`(auto/省略は 1 MiB)を維持します。Feedback の受理・証拠・添付・ZIP ダウンロードは `routePlaintextBytes` と `feedback.budget.maxTransportBytes` で別途宣言します。`*` は完全な1パス区間のみ一致し、ホストの明示値が優先です。ハンドシェイク、adapter-node、プロキシは経路の最大値を使用します。HTTP アダプターは個別宣言された 1 MiB 超の資料経路を最大2件、通常業務を別枠で16件まで許可し、応答完了・失敗時に解放します。混雑は503 / `hai:crypto:072` を返し、業務書込は自動再送しません。
228
+
227
229
  serv/kit は要求の結合、署名、MAC を検証した後、時間窓を超えた報文に暗号化した HTTP 400 時刻エラーを返します。クライアントは `TransportTimeError`(`hai:crypto:071`)で端末とサーバーの時刻同期・再読み込みを案内します。`api-client` は `VALIDATION_FAILED` に変換し `cause.transportCode` に原因を保持します。自動再送やリプレイ時間窓の緩和は行わず、改ざん・未認証の要求は従来の認証エラーです。
228
230
 
229
231
  #### `_kit.yml` による共通 transport 設定
@@ -364,6 +366,8 @@ kit.auth.createApi(iam).createCaptcha を POST /api/auth/captcha として公開
364
366
 
365
367
  設定 transport.maxPlaintextBytes は 1 KiB~64 MiB の明示整数または auto を受け付けます。auto/省略は undefined に正規化され、要件なしの既定は 1 MiB、フィードバックのホストでは crypto.resolveTransportBodyLimits の requiredPlaintextBytes で統合します。明示上限の不足は起動/梱包時の失敗とし、上書きしません。プロキシ・adapter-node・ハンドシェイク・実行時の予算を一致させ、添付・展開・行数の制限は独立して検証します。
366
368
 
369
+ 重要操作では `reauthorize: () => sensitiveActions.authorize(operation)` を Kit apiFetch の第3引数、または typed client の options.context に渡します。セッション更新・復帰または明確な証明拒否の後、新しい用途証明で1回だけ続行します。取消時は追加の書込を送らず、コールバックがなければ旧証明を再送しません。
370
+
367
371
  ## 開発とテスト
368
372
 
369
373
  ```bash
package/README.md CHANGED
@@ -224,6 +224,8 @@ transport 默认作用于同源 `/api/*` endpoint 与 SvelteKit `__data.json`
224
224
 
225
225
  业务容量通过 `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)。
226
226
 
227
+ 普通业务仍使用 `transport.maxPlaintextBytes`(auto/省略为 1 MiB);Feedback 的报告受理、证据、附件及 ZIP 下载路由通过 `routePlaintextBytes` 单独声明 `feedback.budget.maxTransportBytes`。路径中的 `*` 仅匹配一个完整路径段;宿主显式路由值优先。握手、adapter-node 与代理使用各路由最大容量,普通业务不会随材料预算扩大。HTTP 适配器为单独声明且大于 1 MiB 的材料路由保留最多 2 个槽,普通业务另有 16 个槽,完整响应/失败后释放;忙碌返回 503 / `hai:crypto:072`,不自动重放业务写入。
228
+
227
229
  serv/kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的报文返回加密的 HTTP 400 时间错误;客户端抛 `TransportTimeError`(`hai:crypto:071`),提示同步设备和服务器时间后刷新重试。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 保留该原因。不会自动重放请求或放宽防重放窗口;篡改或未认证报文仍返回通用认证失败。
228
230
 
229
231
  #### 使用 `_kit.yml` 统一 transport 配置
@@ -364,6 +366,8 @@ audit 已初始化时,createHandle 为业务 audit.log 绑定已验证会话
364
366
 
365
367
  配置 `transport.maxPlaintextBytes` 支持 1 KiB~64 MiB 的显式整数或 `auto`。auto/省略在解析后为 undefined:无模块需求时仍为 1 MiB,反馈宿主使用 crypto.resolveTransportBodyLimits 的 requiredPlaintextBytes 统一推导。显式上限不足时启动/打包失败,不能被反馈覆盖;代理、adapter-node、握手与运行时使用一致预算。业务附件、解压和数据行数上限继续独立校验。
366
368
 
369
+ 敏感调用通过本次 `reauthorize: () => sensitiveActions.authorize(operation)` 重新确认用途:Kit 放 apiFetch 第三参数,typed client 放 options.context。会话刷新/恢复或明确的再认证拒绝后仅续发一次;取消不写入,未提供回调不重放旧证明。
370
+
367
371
  ## 开发测试
368
372
 
369
373
  ```bash
package/dist/browser.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { c as createKitClient, a as createKitAuthActions, i as installBrowserTransport, b as installBrowserTransportFetch, s as setBrowserToken, d as clearBrowserToken } from './kit-client-auth-Dr0i5Swn.js';
2
- export { C as ClientTransportConfig, K as KitClient, e as KitClientConfig, f as KitConfig, g as KitConfigInput, h as KitConfigSchema, j as KitServerConfig, k as KitServerConfigInput, l as KitTransportConfig, m as KitTransportConfigInput, r as resolveKitConfig, n as resolveKitTransportConfig } from './kit-client-auth-Dr0i5Swn.js';
1
+ import { c as createKitClient, a as createKitAuthActions, i as installBrowserTransport, b as installBrowserTransportFetch, s as setBrowserToken, d as clearBrowserToken } from './kit-client-auth-DZOoQxlp.js';
2
+ export { C as ClientTransportConfig, K as KitClient, e as KitClientConfig, f as KitConfig, g as KitConfigInput, h as KitConfigSchema, j as KitServerConfig, k as KitServerConfigInput, l as KitTransportConfig, m as KitTransportConfigInput, r as resolveKitConfig, n as resolveKitTransportConfig } from './kit-client-auth-DZOoQxlp.js';
3
3
  import { d as defineCrud, s as setAllModulesLocale } from './kit-i18n-DEzmzE5T.js';
4
- export { A as ApiResponse, a as AuthOperations, b as AuthOperationsProvider, C as CorsConfig, F as FormError, c as FormValidationResult, G as GuardConfig, d as GuardResult, H as HaiRequestEvent, e as HandleA2AConfig, f as HandleA2AOperations, g as HandleAuthConfig, h as HandleConfig, i as HookCryptoConfig, M as Middleware, j as MiddlewareContext, R as RateLimitConfig, k as RouteGuard, S as SessionData, l as SessionLike, T as TransportEncryptionOptions } from './kit-types-B6rjOQWx.js';
4
+ export { A as ApiResponse, a as AuthOperations, b as AuthOperationsProvider, C as CorsConfig, F as FormError, c as FormValidationResult, G as GuardConfig, d as GuardResult, H as HaiRequestEvent, e as HandleA2AConfig, f as HandleA2AOperations, g as HandleAuthConfig, h as HandleConfig, i as HookCryptoConfig, M as Middleware, j as MiddlewareContext, R as RateLimitConfig, k as RouteGuard, S as SessionData, l as SessionLike, T as TransportEncryptionOptions } from './kit-types-yogJY1i6.js';
5
5
  import '@h-ai/api-contract';
6
6
  import '@h-ai/core';
7
7
  import '@h-ai/crypto';
package/dist/browser.js CHANGED
@@ -1,8 +1,8 @@
1
- import { defineCrud } from './chunk-5O43CLW3.js';
2
- export { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig } from './chunk-5O43CLW3.js';
3
- import { clearBrowserToken, setBrowserToken, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient } from './chunk-6UCUIS35.js';
1
+ import { defineCrud } from './chunk-TYZ3S2AY.js';
2
+ export { KitConfigSchema, resolveKitConfig, resolveKitTransportConfig } from './chunk-TYZ3S2AY.js';
3
+ import { clearBrowserToken, setBrowserToken, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient } from './chunk-QPJ5AWXX.js';
4
4
  import './chunk-XC7I2L3G.js';
5
- import { setAllModulesLocale } from './chunk-OVRZIDCD.js';
5
+ import { setAllModulesLocale } from './chunk-KRR7QGMN.js';
6
6
 
7
7
  // src/browser.ts
8
8
  var kit = {
@@ -1,6 +1,6 @@
1
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, TransportTimeError, createTransportErrorResponse, TransportBodyTooLargeError, readTransportBody, TRANSPORT_BODY_LIMITS, isValidSM2PublicKey } from '@h-ai/crypto';
2
+ import { kitM } from './chunk-KRR7QGMN.js';
3
+ import { createTransportBodyPolicy, TRANSPORT_PROTOCOL, authenticateTransportRequest, TransportTimeError, createTransportErrorResponse, TransportBodyTooLargeError, readTransportBody, TRANSPORT_BODY_LIMITS, isValidSM2PublicKey } from '@h-ai/crypto';
4
4
 
5
5
  var ENCRYPTABLE_RESPONSE_CONTENT_TYPES = ["application/json", "text/sveltekit-data"];
6
6
  function transportEncryptionMiddleware(config) {
@@ -9,10 +9,11 @@ function transportEncryptionMiddleware(config) {
9
9
  }
10
10
  const keyExchangePath = config.keyExchangePath ?? DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH;
11
11
  const excludePaths = config.excludePaths ?? [];
12
+ const policy = createTransportBodyPolicy(config.maxPlaintextBytes, config.routePlaintextBytes);
12
13
  const result = config.crypto.transport.createServer({
13
14
  serverKeyRing: config.serverKeyRing,
14
15
  keyStore: config.keyStore,
15
- maxPlaintextBytes: config.maxPlaintextBytes
16
+ maxPlaintextBytes: policy.maximum.plaintextBytes
16
17
  });
17
18
  if (!result.success) {
18
19
  return createUnavailableTransportMiddleware(keyExchangePath, excludePaths);
@@ -33,60 +34,67 @@ function transportEncryptionMiddleware(config) {
33
34
  const clientId = event.request.headers.get(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER);
34
35
  if (!clientId)
35
36
  return jsonError(kitM("kit_transportClientIdRequired"), 400);
36
- let requestContext;
37
+ const lease = policy.acquire(pathname);
38
+ if (!lease)
39
+ return jsonError(kitM("kit_transportBusy"), 503);
37
40
  try {
38
- const verified = await authenticateTransportRequest(manager, event.request);
39
- if (!verified.success) {
40
- if (verified.error.cause instanceof TransportTimeError && verified.error.cause.context)
41
- return createTransportErrorResponse(verified.error.cause, { manager, context: verified.error.cause.context });
42
- return jsonError(kitM("kit_transportDecryptFailed"), 400);
41
+ let requestContext;
42
+ try {
43
+ const verified = await authenticateTransportRequest(manager, event.request, lease.limits);
44
+ if (!verified.success) {
45
+ if (verified.error.cause instanceof TransportTimeError && verified.error.cause.context)
46
+ return createTransportErrorResponse(verified.error.cause, { manager, context: verified.error.cause.context });
47
+ return jsonError(kitM("kit_transportDecryptFailed"), 400);
48
+ }
49
+ requestContext = verified.data.context;
50
+ if (event.request.method !== "GET" && event.request.method !== "HEAD")
51
+ replaceRequestBody(event, verified.data.plaintext);
52
+ } catch (error) {
53
+ if (error instanceof TransportBodyTooLargeError)
54
+ return createTransportErrorResponse(error);
55
+ return jsonError(kitM("kit_transportInvalidPayload"), error instanceof RangeError ? 413 : 400);
43
56
  }
44
- requestContext = verified.data.context;
45
- if (event.request.method !== "GET" && event.request.method !== "HEAD")
46
- replaceRequestBody(event, verified.data.plaintext);
47
- } catch (error) {
48
- if (error instanceof TransportBodyTooLargeError)
49
- return createTransportErrorResponse(error);
50
- return jsonError(kitM("kit_transportInvalidPayload"), error instanceof RangeError ? 413 : 400);
51
- }
52
- const response = await next();
53
- const bodyless = event.request.method === "HEAD" || isEmptyResponse(response);
54
- if (bodyless)
55
- void response.body?.cancel().catch(() => {
56
- });
57
- const contentType = response.headers.get("Content-Type") ?? "";
58
- if (!bodyless && !canEncryptResponseContentType(contentType)) {
59
- void response.body?.cancel().catch(() => {
60
- });
61
- return jsonError(kitM("kit_transportEncryptFailed"), 500);
62
- }
63
- try {
64
- const responseBody = bodyless ? "" : await readTransportBody(response.body, {
65
- maxBytes: manager.bodyLimits.plaintextBytes,
66
- direction: "response",
67
- signal: event.request.signal
68
- });
69
- const headers = new Headers(response.headers);
70
- headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
71
- headers.delete(TRANSPORT_PROTOCOL.ENVELOPE_HEADER);
72
- headers.delete("Content-Length");
73
- headers.delete("Content-Encoding");
74
- const encryptedPayload = await manager.encryptResponse(requestContext, responseBody, response.status);
75
- if (!encryptedPayload.success) {
57
+ const response = await next();
58
+ const bodyless = event.request.method === "HEAD" || isEmptyResponse(response);
59
+ if (bodyless)
60
+ void response.body?.cancel().catch(() => {
61
+ });
62
+ const contentType = response.headers.get("Content-Type") ?? "";
63
+ if (!bodyless && !canEncryptResponseContentType(contentType)) {
64
+ void response.body?.cancel().catch(() => {
65
+ });
76
66
  return jsonError(kitM("kit_transportEncryptFailed"), 500);
77
67
  }
78
- headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE);
79
- if (bodyless)
80
- headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encryptedPayload.data));
81
- return new Response(bodyless ? null : JSON.stringify(encryptedPayload.data), {
82
- status: response.status,
83
- statusText: response.statusText,
84
- headers
85
- });
86
- } catch (error) {
87
- if (error instanceof TransportBodyTooLargeError)
88
- return createTransportErrorResponse(error, { manager, context: requestContext });
89
- return jsonError(kitM("kit_transportEncryptFailed"), 500);
68
+ try {
69
+ const responseBody = bodyless ? "" : await readTransportBody(response.body, {
70
+ maxBytes: lease.limits.plaintextBytes,
71
+ direction: "response",
72
+ signal: event.request.signal
73
+ });
74
+ const headers = new Headers(response.headers);
75
+ headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
76
+ headers.delete(TRANSPORT_PROTOCOL.ENVELOPE_HEADER);
77
+ headers.delete("Content-Length");
78
+ headers.delete("Content-Encoding");
79
+ const encryptedPayload = await manager.encryptResponse(requestContext, responseBody, response.status);
80
+ if (!encryptedPayload.success) {
81
+ return jsonError(kitM("kit_transportEncryptFailed"), 500);
82
+ }
83
+ headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE);
84
+ if (bodyless)
85
+ headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encryptedPayload.data));
86
+ return new Response(bodyless ? null : JSON.stringify(encryptedPayload.data), {
87
+ status: response.status,
88
+ statusText: response.statusText,
89
+ headers
90
+ });
91
+ } catch (error) {
92
+ if (error instanceof TransportBodyTooLargeError)
93
+ return createTransportErrorResponse(error, { manager, context: requestContext });
94
+ return jsonError(kitM("kit_transportEncryptFailed"), 500);
95
+ }
96
+ } finally {
97
+ lease.release();
90
98
  }
91
99
  };
92
100
  }
@@ -168,5 +176,5 @@ function replaceRequestBody(event, body) {
168
176
  }
169
177
 
170
178
  export { transportEncryptionMiddleware };
171
- //# sourceMappingURL=chunk-UTCS6FR7.js.map
172
- //# sourceMappingURL=chunk-UTCS6FR7.js.map
179
+ //# sourceMappingURL=chunk-726DCCEK.js.map
180
+ //# sourceMappingURL=chunk-726DCCEK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/modules/crypto/kit-transport-middleware.ts"],"names":[],"mappings":"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","file":"chunk-726DCCEK.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, createTransportBodyPolicy, createTransportErrorResponse, isValidSM2PublicKey, readTransportBody, TRANSPORT_BODY_LIMITS, TRANSPORT_PROTOCOL, TransportBodyTooLargeError, TransportTimeError } 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 const policy = createTransportBodyPolicy(config.maxPlaintextBytes, config.routePlaintextBytes)\n\n // 初始化传输加密管理器\n const result = config.crypto.transport.createServer({\n serverKeyRing: config.serverKeyRing,\n keyStore: config.keyStore,\n maxPlaintextBytes: policy.maximum.plaintextBytes,\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 const lease = policy.acquire(pathname)\n if (!lease)\n return jsonError(kitM('kit_transportBusy'), 503)\n try {\n // ── 解密请求 ──\n // 不依赖可伪造/省略的 Content-Length/Type 判空,非 null 流必须进入实际内容检查。\n let requestContext: TransportMessageContext\n try {\n const verified = await authenticateTransportRequest(manager, event.request, lease.limits)\n if (!verified.success) {\n if (verified.error.cause instanceof TransportTimeError && verified.error.cause.context)\n return createTransportErrorResponse(verified.error.cause, { manager, context: verified.error.cause.context })\n return jsonError(kitM('kit_transportDecryptFailed'), 400)\n }\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 if (error instanceof TransportBodyTooLargeError)\n return createTransportErrorResponse(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: lease.limits.plaintextBytes,\n direction: 'response',\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 (error) {\n if (error instanceof TransportBodyTooLargeError)\n return createTransportErrorResponse(error, { manager, context: requestContext })\n return jsonError(kitM('kit_transportEncryptFailed'), 500)\n }\n }\n finally { lease.release() }\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, maxPlaintextBytes: manager.bodyLimits.plaintextBytes }),\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"]}
@@ -53,7 +53,9 @@ var en_US_default = {
53
53
  kit_requiredPermissions: "Required permissions: {permissions}",
54
54
  kit_idRequired: "ID is required",
55
55
  kit_rateLimitWindowInvalid: "The rate limit window must be an integer from 1 to 2147483647 milliseconds",
56
- kit_rateLimitMemoryCapacity: "The development memory rate limit store is full; use a shared backend"
56
+ kit_rateLimitMemoryCapacity: "The development memory rate limit store is full; use a shared backend",
57
+ kit_transportBusy: "Material processing is busy. Please retry shortly.",
58
+ kit_feedbackUnavailable: "Feedback is temporarily unavailable. Business features remain available."
57
59
  };
58
60
 
59
61
  // messages/ja-JP.json
@@ -107,7 +109,9 @@ var ja_JP_default = {
107
109
  kit_requiredPermissions: "\u5FC5\u8981\u306A\u6A29\u9650: {permissions}",
108
110
  kit_idRequired: "ID\u306F\u5FC5\u9808\u3067\u3059",
109
111
  kit_rateLimitWindowInvalid: "\u30EC\u30FC\u30C8\u5236\u9650\u306E\u671F\u9593\u306F1\u301C2147483647\u30DF\u30EA\u79D2\u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044",
110
- kit_rateLimitMemoryCapacity: "\u958B\u767A\u7528\u30E1\u30E2\u30EA\u30FC\u30EC\u30FC\u30C8\u5236\u9650\u30B9\u30C8\u30A2\u304C\u6E80\u676F\u3067\u3059\u3002\u5171\u6709\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"
112
+ kit_rateLimitMemoryCapacity: "\u958B\u767A\u7528\u30E1\u30E2\u30EA\u30FC\u30EC\u30FC\u30C8\u5236\u9650\u30B9\u30C8\u30A2\u304C\u6E80\u676F\u3067\u3059\u3002\u5171\u6709\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044",
113
+ kit_transportBusy: "\u8CC7\u6599\u306E\u51E6\u7406\u304C\u6DF7\u307F\u5408\u3063\u3066\u3044\u307E\u3059\u3002\u3057\u3070\u3089\u304F\u3057\u3066\u304B\u3089\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
114
+ kit_feedbackUnavailable: "\u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF\u306F\u4E00\u6642\u7684\u306B\u5229\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u696D\u52D9\u6A5F\u80FD\u306F\u5F15\u304D\u7D9A\u304D\u5229\u7528\u3067\u304D\u307E\u3059\u3002"
111
115
  };
112
116
 
113
117
  // messages/zh-CN.json
@@ -161,7 +165,9 @@ var zh_CN_default = {
161
165
  kit_requiredPermissions: "\u9700\u8981\u6743\u9650\uFF1A{permissions}",
162
166
  kit_idRequired: "ID \u4E0D\u80FD\u4E3A\u7A7A",
163
167
  kit_rateLimitWindowInvalid: "\u9650\u6D41\u7A97\u53E3\u5FC5\u987B\u4E3A 1 \u81F3 2147483647 \u7684\u6574\u6570\u6BEB\u79D2",
164
- kit_rateLimitMemoryCapacity: "\u5F00\u53D1\u5185\u5B58\u9650\u6D41\u5BB9\u91CF\u5DF2\u6EE1\uFF0C\u8BF7\u4F7F\u7528\u5171\u4EAB\u540E\u7AEF"
168
+ kit_rateLimitMemoryCapacity: "\u5F00\u53D1\u5185\u5B58\u9650\u6D41\u5BB9\u91CF\u5DF2\u6EE1\uFF0C\u8BF7\u4F7F\u7528\u5171\u4EAB\u540E\u7AEF",
169
+ kit_transportBusy: "\u6750\u6599\u5904\u7406\u7E41\u5FD9\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
170
+ kit_feedbackUnavailable: "\u95EE\u9898\u53CD\u9988\u6682\u4E0D\u53EF\u7528\uFF0C\u4E1A\u52A1\u529F\u80FD\u53EF\u7EE7\u7EED\u4F7F\u7528"
165
171
  };
166
172
 
167
173
  // src/kit-i18n.ts
@@ -171,5 +177,5 @@ function setAllModulesLocale(locale) {
171
177
  }
172
178
 
173
179
  export { kitM, setAllModulesLocale };
174
- //# sourceMappingURL=chunk-OVRZIDCD.js.map
175
- //# sourceMappingURL=chunk-OVRZIDCD.js.map
180
+ //# sourceMappingURL=chunk-KRR7QGMN.js.map
181
+ //# sourceMappingURL=chunk-KRR7QGMN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../messages/en-US.json","../messages/ja-JP.json","../messages/zh-CN.json","../src/kit-i18n.ts"],"names":[],"mappings":"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","file":"chunk-KRR7QGMN.js","sourcesContent":["{\n \"kit_a2aAuthFailed\": \"A2A authentication failed\",\n \"kit_a2aParseError\": \"Invalid JSON request body\",\n \"kit_a2aCardUnavailable\": \"Agent card not available\",\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"kit_unauthorized\": \"Unauthorized\",\n \"kit_forbidden\": \"Forbidden: missing {permission} permission\",\n \"kit_authNotConfigured\": \"kit.auth is not configured: pass auth.operations in kit.createHandle()\",\n \"kit_csrfTokenFailed\": \"Failed to generate CSRF token\",\n \"kit_csrfVerifyFailed\": \"CSRF token verification failed\",\n \"kit_csrfValidationFailed\": \"Invalid CSRF token\",\n \"kit_rateLimitExceeded\": \"Too many requests, please try again later\",\n \"kit_unsupportedContentType\": \"Unsupported content type\",\n \"kit_parseBodyFailed\": \"Failed to parse request body\",\n \"kit_encryptFailed\": \"Encryption failed\",\n \"kit_transportKeyGenerationFailed\": \"Transport encryption key generation failed\",\n \"kit_transportClientKeyNotFound\": \"Client key not found\",\n \"kit_transportEncryptFailed\": \"Transport encryption failed\",\n \"kit_transportEncryptKeyFailed\": \"Symmetric key encryption failed\",\n \"kit_transportDecryptKeyFailed\": \"Symmetric key decryption failed\",\n \"kit_transportDecryptFailed\": \"Transport decryption failed\",\n \"kit_transportInvalidPayload\": \"Invalid encrypted payload\",\n \"kit_transportKeyExchangeFailed\": \"Key exchange failed\",\n \"kit_transportClientIdRequired\": \"Missing X-Client-Id header, key exchange is required for transport encryption\",\n \"kit_signFailed\": \"Request signing failed\",\n \"kit_authRequired\": \"Authentication required\",\n \"kit_accessDenied\": \"Access denied\",\n \"kit_resourceNotFound\": \"Resource not found\",\n \"kit_internalError\": \"Internal server error\",\n \"kit_validationFailed\": \"Validation failed\",\n \"kit_validationRequired\": \"This field is required\",\n \"kit_validationInvalid\": \"Invalid input format\",\n \"kit_validationInvalidType\": \"Invalid field type\",\n \"kit_validationStringMin\": \"Enter at least {min} characters\",\n \"kit_validationStringMax\": \"Enter at most {max} characters\",\n \"kit_validationNumberMin\": \"Value must be at least {min}\",\n \"kit_validationNumberMax\": \"Value must be at most {max}\",\n \"kit_validationArrayMin\": \"Select at least {min} item(s)\",\n \"kit_validationArrayMax\": \"Select at most {max} item(s)\",\n \"kit_validationTooSmall\": \"Value must be at least {min}\",\n \"kit_validationTooBig\": \"Value must be at most {max}\",\n \"kit_validationEmail\": \"Enter a valid email address\",\n \"kit_validationUrl\": \"Enter a valid URL\",\n \"kit_validationUuid\": \"Enter a valid UUID\",\n \"kit_validationEnum\": \"Select a valid option\",\n \"kit_noGuardsPassed\": \"No guards passed\",\n \"kit_requiredRoles\": \"Required roles: {roles}\",\n \"kit_requiredPermissions\": \"Required permissions: {permissions}\",\n \"kit_idRequired\": \"ID is required\",\n \"kit_rateLimitWindowInvalid\": \"The rate limit window must be an integer from 1 to 2147483647 milliseconds\",\n \"kit_rateLimitMemoryCapacity\": \"The development memory rate limit store is full; use a shared backend\",\n \"kit_transportBusy\": \"Material processing is busy. Please retry shortly.\",\n \"kit_feedbackUnavailable\": \"Feedback is temporarily unavailable. Business features remain available.\"\n}\n","{\n \"kit_a2aAuthFailed\": \"A2A認証に失敗しました\",\n \"kit_a2aParseError\": \"リクエスト本文が有効なJSONではありません\",\n \"kit_a2aCardUnavailable\": \"エージェントカードを利用できません\",\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"kit_unauthorized\": \"認証されていません\",\n \"kit_forbidden\": \"アクセスが拒否されました: {permission}権限がありません\",\n \"kit_authNotConfigured\": \"kit.authが設定されていません。kit.createHandle()にauth.operationsを渡してください\",\n \"kit_csrfTokenFailed\": \"CSRFトークンの生成に失敗しました\",\n \"kit_csrfVerifyFailed\": \"CSRFトークンの検証に失敗しました\",\n \"kit_csrfValidationFailed\": \"CSRFトークンが無効です\",\n \"kit_rateLimitExceeded\": \"リクエストが多すぎます。しばらくしてから再試行してください\",\n \"kit_unsupportedContentType\": \"未対応のコンテンツタイプです\",\n \"kit_parseBodyFailed\": \"リクエスト本文の解析に失敗しました\",\n \"kit_encryptFailed\": \"暗号化に失敗しました\",\n \"kit_transportKeyGenerationFailed\": \"通信暗号化鍵の生成に失敗しました\",\n \"kit_transportClientKeyNotFound\": \"クライアント鍵が見つかりません\",\n \"kit_transportEncryptFailed\": \"通信の暗号化に失敗しました\",\n \"kit_transportEncryptKeyFailed\": \"対称鍵の暗号化に失敗しました\",\n \"kit_transportDecryptKeyFailed\": \"対称鍵の復号に失敗しました\",\n \"kit_transportDecryptFailed\": \"通信の復号に失敗しました\",\n \"kit_transportInvalidPayload\": \"暗号化ペイロードが無効です\",\n \"kit_transportKeyExchangeFailed\": \"鍵交換に失敗しました\",\n \"kit_transportClientIdRequired\": \"X-Client-Idヘッダーがありません。通信暗号化には鍵交換が必要です\",\n \"kit_signFailed\": \"リクエストの署名に失敗しました\",\n \"kit_authRequired\": \"認証が必要です\",\n \"kit_accessDenied\": \"アクセスが拒否されました\",\n \"kit_resourceNotFound\": \"リソースが見つかりません\",\n \"kit_internalError\": \"サーバー内部エラー\",\n \"kit_validationFailed\": \"入力内容の検証に失敗しました\",\n \"kit_validationRequired\": \"この項目は必須です\",\n \"kit_validationInvalid\": \"入力形式が無効です\",\n \"kit_validationInvalidType\": \"フィールドの型が無効です\",\n \"kit_validationStringMin\": \"{min}文字以上で入力してください\",\n \"kit_validationStringMax\": \"{max}文字以内で入力してください\",\n \"kit_validationNumberMin\": \"{min}以上の値を入力してください\",\n \"kit_validationNumberMax\": \"{max}以下の値を入力してください\",\n \"kit_validationArrayMin\": \"{min}件以上選択してください\",\n \"kit_validationArrayMax\": \"{max}件以内で選択してください\",\n \"kit_validationTooSmall\": \"{min}以上の値を入力してください\",\n \"kit_validationTooBig\": \"{max}以下の値を入力してください\",\n \"kit_validationEmail\": \"有効なメールアドレスを入力してください\",\n \"kit_validationUrl\": \"有効なURLを入力してください\",\n \"kit_validationUuid\": \"有効なUUIDを入力してください\",\n \"kit_validationEnum\": \"有効な選択肢を選んでください\",\n \"kit_noGuardsPassed\": \"どのガードも通過しませんでした\",\n \"kit_requiredRoles\": \"必要なロール: {roles}\",\n \"kit_requiredPermissions\": \"必要な権限: {permissions}\",\n \"kit_idRequired\": \"IDは必須です\",\n \"kit_rateLimitWindowInvalid\": \"レート制限の期間は1〜2147483647ミリ秒の整数で指定してください\",\n \"kit_rateLimitMemoryCapacity\": \"開発用メモリーレート制限ストアが満杯です。共有バックエンドを使用してください\",\n \"kit_transportBusy\": \"資料の処理が混み合っています。しばらくしてから再試行してください。\",\n \"kit_feedbackUnavailable\": \"フィードバックは一時的に利用できません。業務機能は引き続き利用できます。\"\n}\n","{\n \"kit_a2aAuthFailed\": \"A2A 身份认证失败\",\n \"kit_a2aParseError\": \"请求体不是有效的 JSON\",\n \"kit_a2aCardUnavailable\": \"Agent Card 不可用\",\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"kit_unauthorized\": \"未授权\",\n \"kit_forbidden\": \"权限不足:缺少 {permission} 权限\",\n \"kit_authNotConfigured\": \"kit.auth 未配置:请在 kit.createHandle() 中传入 auth.operations\",\n \"kit_csrfTokenFailed\": \"生成 CSRF Token 失败\",\n \"kit_csrfVerifyFailed\": \"CSRF Token 验证失败\",\n \"kit_csrfValidationFailed\": \"CSRF Token 无效\",\n \"kit_rateLimitExceeded\": \"请求过于频繁,请稍后再试\",\n \"kit_unsupportedContentType\": \"不支持的内容类型\",\n \"kit_parseBodyFailed\": \"请求体解析失败\",\n \"kit_encryptFailed\": \"加密失败\",\n \"kit_transportKeyGenerationFailed\": \"传输加密密钥对生成失败\",\n \"kit_transportClientKeyNotFound\": \"客户端密钥未找到\",\n \"kit_transportEncryptFailed\": \"传输加密失败\",\n \"kit_transportEncryptKeyFailed\": \"对称密钥加密失败\",\n \"kit_transportDecryptKeyFailed\": \"对称密钥解密失败\",\n \"kit_transportDecryptFailed\": \"传输解密失败\",\n \"kit_transportInvalidPayload\": \"无效的加密载荷\",\n \"kit_transportKeyExchangeFailed\": \"密钥交换失败\",\n \"kit_transportClientIdRequired\": \"缺少 X-Client-Id 请求头,传输加密必须先完成密钥交换\",\n \"kit_signFailed\": \"请求签名失败\",\n \"kit_authRequired\": \"需要身份认证\",\n \"kit_accessDenied\": \"拒绝访问\",\n \"kit_resourceNotFound\": \"资源未找到\",\n \"kit_internalError\": \"内部服务器错误\",\n \"kit_validationFailed\": \"数据验证失败\",\n \"kit_validationRequired\": \"此字段必填\",\n \"kit_validationInvalid\": \"输入格式无效\",\n \"kit_validationInvalidType\": \"字段类型不正确\",\n \"kit_validationStringMin\": \"至少输入 {min} 个字符\",\n \"kit_validationStringMax\": \"最多输入 {max} 个字符\",\n \"kit_validationNumberMin\": \"数值不能小于 {min}\",\n \"kit_validationNumberMax\": \"数值不能大于 {max}\",\n \"kit_validationArrayMin\": \"至少选择 {min} 项\",\n \"kit_validationArrayMax\": \"最多选择 {max} 项\",\n \"kit_validationTooSmall\": \"不能小于 {min}\",\n \"kit_validationTooBig\": \"不能大于 {max}\",\n \"kit_validationEmail\": \"请输入有效的邮箱地址\",\n \"kit_validationUrl\": \"请输入有效的网址\",\n \"kit_validationUuid\": \"请输入有效的 UUID\",\n \"kit_validationEnum\": \"请选择有效选项\",\n \"kit_noGuardsPassed\": \"没有守卫通过\",\n \"kit_requiredRoles\": \"需要角色:{roles}\",\n \"kit_requiredPermissions\": \"需要权限:{permissions}\",\n \"kit_idRequired\": \"ID 不能为空\",\n \"kit_rateLimitWindowInvalid\": \"限流窗口必须为 1 至 2147483647 的整数毫秒\",\n \"kit_rateLimitMemoryCapacity\": \"开发内存限流容量已满,请使用共享后端\",\n \"kit_transportBusy\": \"材料处理繁忙,请稍后重试\",\n \"kit_feedbackUnavailable\": \"问题反馈暂不可用,业务功能可继续使用\"\n}\n","/**\n * @h-ai/kit — i18n 工具\n *\n * Kit 模块的国际化消息工具,从 @h-ai/core 调用 i18n API。\n * @module kit-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesJaJP from '../messages/ja-JP.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\n/**\n * Kit 中英日消息字典的合法 key 联合。\n * @example 模块内部上下文\n * ```ts\n * const key: KitMessageKey = 'kit_accessDenied'\n * const message = kitM(key)\n * void message\n * ```\n */\ntype KitMessageKey = keyof typeof messagesZhCN\n\n/**\n * 获取 Kit 模块的 i18n 消息\n * @example 模块内部上下文\n * ```ts\n * const message = kitM('kit_accessDenied', { locale: 'en-US' })\n * void message\n * ```\n */\nexport const kitM\n = core.i18n.createMessageGetter<KitMessageKey>({ 'zh-CN': messagesZhCN, 'en-US': messagesEnUS, 'ja-JP': messagesJaJP })\n\n/**\n * 设置浏览器应用语言或 Node 启动默认语言;请求中只更新当前 runWithLocale 上下文。\n *\n * 通过 @h-ai/core 的集中式 locale 管理器,一次调用即可同步所有模块。\n * 各模块的 createMessageGetter 优先读取当前 Node 请求上下文;SSR 必须包裹实际 resolve。\n * @param locale - 支持的应用默认语言;请求中应使用上下文隔离。\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * // 应用启动时设置默认语言;请求内使用 runWithLocale。\n * kit.i18n.setLocale('en-US')\n * ```\n */\nexport function setAllModulesLocale(locale: string): void {\n core.i18n.setGlobalLocale(locale)\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { resolveTransportPath, DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH, shouldUseTransportForUrl } from './chunk-XC7I2L3G.js';
2
- import { kitM } from './chunk-OVRZIDCD.js';
2
+ import { kitM } from './chunk-KRR7QGMN.js';
3
3
  import process from 'process';
4
4
  import { ok, core, err } from '@h-ai/core';
5
5
  import { IamSessionErrorCodeSchema, apiContract, IamReauthenticationProofSchema, IamPublicConfigSchema, IamCaptchaChallengeSchema } from '@h-ai/api-contract';
@@ -256,17 +256,32 @@ function createKitClient(config = {}) {
256
256
  return {
257
257
  async apiFetch(input, init = {}, options) {
258
258
  const enabled = config.recoverSession && options?.recoverSession !== false;
259
- const original = enabled ? new Request(input instanceof Request ? input : new URL(String(input), getCurrentOrigin()), init) : void 0;
259
+ const original = enabled || options?.reauthorize ? new Request(input instanceof Request ? input : new URL(String(input), getCurrentOrigin()), init) : void 0;
260
260
  const retry = original?.clone();
261
261
  const response = await performFetch(original ?? input, original ? {} : init);
262
- if (!enabled || response.status !== 401 || !retry || retry.signal.aborted || new URL(retry.url).origin !== getCurrentOrigin())
262
+ if (!retry || retry.signal.aborted || new URL(retry.url).origin !== getCurrentOrigin())
263
+ return response;
264
+ if (response.status !== 401 && !options?.reauthorize)
265
+ return response;
266
+ if (response.status !== 401 && !response.headers.get("content-type")?.includes("application/json"))
263
267
  return response;
264
268
  const failure = await response.clone().json().catch(() => void 0);
265
- if (failure && typeof failure === "object" && "error" in failure && failure.error && typeof failure.error === "object" && "code" in failure.error && typeof failure.error.code === "string" && failure.error.code.startsWith("hai:iam:") && !IamSessionErrorCodeSchema.safeParse(failure.error.code).success) {
269
+ const code = failure && typeof failure === "object" && "success" in failure && failure.success === false && "error" in failure && failure.error && typeof failure.error === "object" && "code" in failure.error ? failure.error.code : void 0;
270
+ const proofRejected = code === "hai:iam:027" && options?.reauthorize;
271
+ if (!proofRejected && (!enabled || response.status !== 401 || typeof code === "string" && code.startsWith("hai:iam:") && !IamSessionErrorCodeSchema.safeParse(code).success))
266
272
  return response;
267
- }
268
- if (!await config.recoverSession())
273
+ if (!proofRejected && !await config.recoverSession())
269
274
  return response;
275
+ if (retry.headers.has("x-hai-reauthentication") || options?.reauthorize) {
276
+ if (!options?.reauthorize)
277
+ return response;
278
+ const ticket = await options.reauthorize();
279
+ if (ticket === null || retry.signal.aborted)
280
+ return response;
281
+ retry.headers.delete("x-hai-reauthentication");
282
+ if (ticket)
283
+ retry.headers.set("x-hai-reauthentication", ticket);
284
+ }
270
285
  return performFetch(retry);
271
286
  },
272
287
  get ready() {
@@ -339,5 +354,5 @@ function createKitAuthActions(options = {}) {
339
354
  }
340
355
 
341
356
  export { clearBrowserToken, configureAuth, createKitAuthActions, createKitClient, getAccessToken, installBrowserTransport, installBrowserTransportFetch, login, loginWithApiKey, loginWithLdap, loginWithOtp, logout, registerAndLogin, setBrowserToken };
342
- //# sourceMappingURL=chunk-6UCUIS35.js.map
343
- //# sourceMappingURL=chunk-6UCUIS35.js.map
357
+ //# sourceMappingURL=chunk-QPJ5AWXX.js.map
358
+ //# sourceMappingURL=chunk-QPJ5AWXX.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","options","core"],"mappings":"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","file":"chunk-QPJ5AWXX.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, AuthRequestContext, 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 * @param context - 宿主提供的可信登录元数据,不从凭据输入复制 IP。\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 context?: AuthRequestContext,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.login(input, context))\n}\n\n/**\n * 服务端登录(OTP 验证码):内部调用 iam.auth.loginWithOtp,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - OTP 凭证(identifier + code)\n * @param context - 宿主提供的可信登录元数据。\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 context?: AuthRequestContext,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithOtp(input, context))\n}\n\n/**\n * 服务端登录(LDAP):内部调用 iam.auth.loginWithLdap,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - LDAP 凭证(username + password)\n * @param context - 宿主提供的可信登录元数据。\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 context?: AuthRequestContext,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithLdap(input, context))\n}\n\n/**\n * 服务端登录(API Key):内部调用 iam.auth.loginWithApiKey,成功时自动写入 Token Cookie。\n *\n * @param cookies - SvelteKit cookies 对象\n * @param credentials - API Key 凭证(key)\n * @param context - 宿主提供的可信登录元数据。\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 context?: AuthRequestContext,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, credentials, (ops, input) => ops.loginWithApiKey(input, context))\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 context?: AuthRequestContext,\n): Promise<HaiResult<AuthResult>> {\n return runAuthLogin(cookies, options, (ops, input) => ops.registerAndLogin(input, context))\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 { IamSessionErrorCodeSchema } from '@h-ai/api-contract'\nimport { core } from '@h-ai/core'\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 /** 可选本地明文上限;缺省跟随服务端握手。 */\n maxPlaintextBytes?: number\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 recoverSession?: () => Promise<boolean>\n /** 会话终端分组,默认web;服务端按终端与全局配额共同限制。 */\n terminalType?: 'web' | 'app' | 'desktop' | 'api'\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: (input: RequestInfo | URL, init?: RequestInit, options?: { recoverSession?: boolean, reauthorize?: () => Promise<string | null | undefined> }) => 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 * 判断某个 `Request` 的有效方法、请求头与原始正文是否可走传输加密。\n *\n * `Request.body` 已转换为流,必须结合 Content-Type 识别可重注入的 JSON,\n * 避免把 multipart 等二进制正文当文本加密。纯 BodyInit 则仍按原始类型判定。\n */\nfunction canUseTransportForRequest(request: Pick<Request, 'method' | 'headers' | 'body'>): 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 maxPlaintextBytes: transportConfig.maxPlaintextBytes,\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 maxPlaintextBytes: transportConfig.maxPlaintextBytes,\n })\n : null\n\n /**\n * 统一 API fetch\n */\n async function performFetch(input: RequestInfo | URL, init: RequestInit = {}): Promise<Response> {\n const request = input instanceof Request ? input : undefined\n const method = (init.method ?? request?.method ?? 'GET').toUpperCase()\n const isWriteMethod = !['GET', 'HEAD', 'OPTIONS'].includes(method)\n const headers = new Headers(init.headers ?? request?.headers)\n headers.set('x-hai-terminal', config.terminalType ?? 'web')\n const suppliedId = headers.get('x-request-id')\n const traceId = suppliedId && /^[\\w.:-]{1,128}$/.test(suppliedId) ? suppliedId : core.id.uuid()\n headers.set('x-request-id', traceId)\n // 仅检查元数据,不构建/读取 Request,确保透传上传的流与浏览器边界保持可用。\n const canUseTransport = request && init.body === undefined\n ? canUseTransportForRequest({ method, headers, body: request.body })\n : canEncryptRequestBody(init.body)\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(request?.url ?? (input instanceof URL ? input : String(input)), {\n origin: transportOrigin,\n keyExchangePath: transportKeyExchangePath,\n excludePaths: transportConfig?.excludePaths,\n })\n ) {\n return send(() => transportClient.encryptedFetch(input, { ...init, headers }))\n }\n\n // ── 发送(FormData/Blob/Stream 等保持原样,不破坏浏览器边界) ──\n return send(() => fetch(input, { ...init, headers }))\n\n async function send(operation: () => Promise<Response>): Promise<Response> {\n try {\n const response = await operation()\n if (!response.ok)\n core.logger.error('HTTP request failed', { traceId: response.headers.get('x-trace-id') ?? response.headers.get('x-request-id') ?? traceId, method, status: response.status })\n return response\n }\n catch (error) {\n core.logger.error('HTTP transport failed', { traceId, method, error })\n throw error\n }\n }\n }\n\n return {\n async apiFetch(input, init = {}, options) {\n const enabled = config.recoverSession && options?.recoverSession !== false\n const original = enabled || options?.reauthorize ? new Request(input instanceof Request ? input : new URL(String(input), getCurrentOrigin()), init) : undefined\n const retry = original?.clone()\n const response = await performFetch(original ?? input, original ? {} : init)\n if (!retry || retry.signal.aborted || new URL(retry.url).origin !== getCurrentOrigin())\n return response\n if (response.status !== 401 && !options?.reauthorize)\n return response\n if (response.status !== 401 && !response.headers.get('content-type')?.includes('application/json'))\n return response\n const failure: unknown = await response.clone().json().catch(() => undefined)\n const code = failure && typeof failure === 'object' && 'success' in failure && failure.success === false && 'error' in failure && failure.error && typeof failure.error === 'object' && 'code' in failure.error ? failure.error.code : undefined\n const proofRejected = code === 'hai:iam:027' && options?.reauthorize\n if (!proofRejected && (!enabled || response.status !== 401 || (typeof code === 'string' && code.startsWith('hai:iam:') && !IamSessionErrorCodeSchema.safeParse(code).success)))\n return response\n if (!proofRejected && !await config.recoverSession!())\n return response\n // 新会话不能复用旧用途证明;只在明确拒绝且宿主重新确认后重试一次。\n if (retry.headers.has('x-hai-reauthentication') || options?.reauthorize) {\n if (!options?.reauthorize)\n return response\n const ticket = await options.reauthorize()\n if (ticket === null || retry.signal.aborted)\n return response\n retry.headers.delete('x-hai-reauthentication')\n if (ticket)\n retry.headers.set('x-hai-reauthentication', ticket)\n }\n // 认证动作本身必须传 recoverSession:false,避免错误密码再次等待自己。\n return performFetch(retry)\n },\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 { IamCreateCaptchaInputSchema, IamLoginInput, IamPublicConfig, IamRegisterInput } from '@h-ai/api-contract'\nimport type { HaiResult } from '@h-ai/core'\nimport type { KitClient } from './kit-client.js'\nimport { apiContract, IamCaptchaChallengeSchema, IamPublicConfigSchema, IamReauthenticationProofSchema } from '@h-ai/api-contract'\nimport { core, 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, init: Pick<RequestInit, 'keepalive'> = {}): Promise<HaiResult<unknown>> {\n const response = await apiFetch(`${base}/${path}`, { ...init, method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(input) }, { recoverSession: false })\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 bindPageExit(options: { enabled?: boolean } = {}) {\n if (!options.enabled || typeof window === 'undefined')\n return () => {}\n const target = window\n let sent = false\n const onPageHide = (event: PageTransitionEvent) => {\n if (event.persisted || sent)\n return\n sent = true\n void post('session/close', {}, { keepalive: true }).then((result) => {\n if (!result.success)\n core.logger.warn('Page exit session revocation failed', { errorCode: result.error.code })\n }).catch(() => core.logger.warn('Page exit session request did not complete'))\n }\n target.addEventListener('pagehide', onPageHide)\n return () => target.removeEventListener('pagehide', onPageHide)\n },\n async createCaptcha(input: z.input<typeof IamCreateCaptchaInputSchema>) {\n const result = await post('captcha', input)\n return result.success ? ok(IamCaptchaChallengeSchema.parse(result.data)) : result\n },\n async publicConfig(): Promise<HaiResult<IamPublicConfig | null>> {\n const response = await apiFetch(`${base}/config`, undefined, { recoverSession: false })\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, captcha: input.captcha, expectedUserId: input.expectedUserId })\n if (input.method === 'email' || input.method === 'sms')\n return post('login/otp', { identifier: input.identifier, code: input.code, captcha: input.captcha, expectedUserId: input.expectedUserId })\n return post('login', { identifier: input.identifier, password: input.password, captcha: input.captcha, expectedUserId: input.expectedUserId })\n },\n register: (input: IamRegisterInput) => post('register', { ...input, confirmPassword: input.password }),\n sendOtp: (identifier: string, options?: { captcha?: string }) => post('otp/send', { identifier, ...options }),\n requestPasswordReset: (identifier: string, options?: { captcha?: string }) => post('password-reset/request', { identifier, ...options }),\n confirmPasswordReset: (token: string, newPassword: string) => post('password-reset/confirm', { token, newPassword }),\n renewPassword: (input: { identifier: string, password: string, newPassword: string, captcha?: string }) => post('password/renew', input),\n async reauthenticate(password: string, operation: string) {\n const response = await apiFetch(`${base}/reauthenticate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password, operation }) })\n return apiContract.haiResultSchema(IamReauthenticationProofSchema).parse(await response.json())\n },\n }\n}\n"]}
@@ -75,7 +75,8 @@ var AbsolutePathSchema = z.custom(
75
75
  var KitTransportConfigInputSchema = z.union([
76
76
  z.literal(false),
77
77
  z.object({
78
- maxPlaintextBytes: z.union([z.number().int().min(TRANSPORT_PLAINTEXT_RANGE.min).max(TRANSPORT_PLAINTEXT_RANGE.max), z.literal("auto")]).transform((value) => value === "auto" ? void 0 : value).optional().describe("\u53CC\u5411\u660E\u6587\u4E0A\u9650\uFF1Bauto \u6309\u5DF2\u542F\u7528\u6A21\u5757\u63A8\u5BFC\uFF0C\u65E0\u6A21\u5757\u9700\u6C42\u65F6 1 MiB\uFF1B\u663E\u5F0F\u6574\u6570\u4E0D\u5F97\u5C0F\u4E8E\u6A21\u5757\u9700\u6C42").meta({ default: "auto" }),
78
+ maxPlaintextBytes: z.union([z.number().int().min(TRANSPORT_PLAINTEXT_RANGE.min).max(TRANSPORT_PLAINTEXT_RANGE.max), z.literal("auto")]).transform((value) => value === "auto" ? void 0 : value).optional().describe("\u666E\u901A\u4E1A\u52A1\u53CC\u5411\u660E\u6587\u4E0A\u9650\uFF1Bauto \u4E3A 1 MiB\uFF1B\u5927\u6750\u6599\u4F7F\u7528 routePlaintextBytes \u5355\u72EC\u58F0\u660E").meta({ default: "auto" }),
79
+ routePlaintextBytes: z.record(AbsolutePathSchema, z.number().int().min(TRANSPORT_PLAINTEXT_RANGE.min).max(TRANSPORT_PLAINTEXT_RANGE.max)).optional().describe("\u5B8C\u6574\u8DEF\u5F84\u6BB5\u5230\u53CC\u5411\u660E\u6587\u5B57\u8282\u4E0A\u9650\u7684\u6620\u5C04\uFF1B\u661F\u53F7\u5339\u914D\u5355\u6BB5\uFF0C\u4F9B\u9644\u4EF6\u548C\u5F52\u6863\u72EC\u7ACB\u5BB9\u91CF"),
79
80
  keyExchangePath: AbsolutePathSchema.default("/api/_hai/key-exchange"),
80
81
  excludePaths: z.array(AbsolutePathSchema).default([])
81
82
  })
@@ -116,5 +117,5 @@ function resolveKitConfig(input = {}) {
116
117
  }
117
118
 
118
119
  export { KitConfigSchema, defineCrud, resolveKitConfig, resolveKitTransportConfig, resolveOptions, resolveText };
119
- //# sourceMappingURL=chunk-5O43CLW3.js.map
120
- //# sourceMappingURL=chunk-5O43CLW3.js.map
120
+ //# sourceMappingURL=chunk-TYZ3S2AY.js.map
121
+ //# sourceMappingURL=chunk-TYZ3S2AY.js.map