@h-ai/kit 0.1.0-alpha.53 → 0.1.0-alpha.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +3 -2
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-DXZYOYXZ.js → chunk-CTIN5EQP.js} +4 -3
- package/dist/chunk-CTIN5EQP.js.map +1 -0
- package/dist/chunk-DVW7CQ3H.js +141 -0
- package/dist/chunk-DVW7CQ3H.js.map +1 -0
- package/dist/{chunk-2XRMSVLR.js → chunk-MLSP67JF.js} +4 -3
- package/dist/chunk-MLSP67JF.js.map +1 -0
- package/dist/chunk-NVYIUBU7.js +41 -0
- package/dist/chunk-NVYIUBU7.js.map +1 -0
- package/dist/{chunk-MI65L22O.js → chunk-QHYNL24G.js} +9 -38
- package/dist/chunk-QHYNL24G.js.map +1 -0
- package/dist/client/index.js +3 -2
- package/dist/client/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/{kit-types-BIkoLpfq.d.ts → kit-types-ByWdbCeO.d.ts} +4 -3
- package/dist/modules/a2a/index.d.ts +5 -5
- package/dist/modules/a2a/index.js +2 -55
- package/dist/modules/a2a/index.js.map +1 -1
- package/dist/modules/crypto/index.d.ts +1 -1
- package/dist/modules/crypto/index.js +3 -2
- package/dist/modules/crypto/index.js.map +1 -1
- package/package.json +9 -5
- package/dist/chunk-2XRMSVLR.js.map +0 -1
- package/dist/chunk-475WECWK.js +0 -110
- package/dist/chunk-475WECWK.js.map +0 -1
- package/dist/chunk-DXZYOYXZ.js.map +0 -1
- package/dist/chunk-MI65L22O.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/modules/crypto/kit-crypto-helpers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @h-ai/kit — Crypto Helpers\n *\n * 集成 @h-ai/crypto 的 SvelteKit 工具\n * @module kit-crypto-helpers\n */\n\nimport type { Cookies, RequestEvent } from '@sveltejs/kit'\nimport type {\n CryptoCsrfConfig,\n CryptoServiceLike,\n EncryptedCookieConfig,\n WebhookVerifyConfig,\n} from './kit-crypto-types.js'\nimport { kitM } from '../../kit-i18n.js'\n\n/**\n * 验证 Webhook 签名\n *\n * 从请求头取出签名,用 HMAC 对原始 Body 进行比较验证。\n *\n * @param config - 签名验证配置\n * @returns 签名是否合法\n *\n * @example\n * ```ts\n * const valid = await verifyWebhookSignature({\n * crypto, event, secretKey: 'wh_secret', signatureHeader: 'X-Signature',\n * })\n * if (!valid) return new Response('Invalid signature', { status: 401 })\n * ```\n */\nexport async function verifyWebhookSignature(config: WebhookVerifyConfig): Promise<boolean> {\n const {\n crypto,\n event,\n secretKey,\n signatureHeader = 'X-Signature',\n algorithm = 'sha256',\n } = config\n\n const signature = event.request.headers.get(signatureHeader)\n if (!signature) {\n return false\n }\n\n const body = await event.request.text()\n\n try {\n const result = await crypto.hmac.verify(body, secretKey, signature, algorithm)\n return result.success && result.data === true\n }\n catch {\n return false\n }\n}\n\n/**\n * 生成请求 HMAC 签名(用于调用外部服务)\n *\n * @param crypto - @h-ai/crypto 服务实例\n * @param body - 请求体字符串\n * @param secretKey - 共享密钥\n * @param algorithm - 哈希算法(默认 `'sha256'`)\n * @returns 签名字符串\n * @throws 签名失败时抛出 Error\n */\nexport async function signRequest(\n crypto: CryptoServiceLike,\n body: string,\n secretKey: string,\n algorithm = 'sha256',\n): Promise<string> {\n const result = await crypto.hmac.sign(body, secretKey, algorithm)\n if (!result.success) {\n throw new Error(result.error?.message || kitM('kit_signFailed'))\n }\n return result.data!\n}\n\n/**\n * 创建 CSRF Token 管理器\n *\n * 基于 `@h-ai/crypto` 随机字节 + 时间安全比较,\n * 提供 token 生成、验证和 SvelteKit handle 中间件。\n *\n * @param config - CSRF 配置(crypto 实例、Cookie 名、Header 名等)\n * @returns `{ generate, verify, createHandle }`\n *\n * @example\n * ```ts\n * const csrf = createCsrfManager({ crypto })\n * // 在 load 中生成\n * const token = await csrf.generate(cookies)\n * // 在 action 中验证\n * const ok = await csrf.verify(event)\n * ```\n */\nexport function createCsrfManager(config: CryptoCsrfConfig) {\n const {\n crypto,\n cookieName = 'csrf_token',\n headerName = 'X-CSRF-Token',\n formFieldName = '_csrf',\n tokenLength = 32,\n cookieOptions = {},\n } = config\n\n return {\n /**\n * 生成新的 CSRF Token 并设置 Cookie\n */\n async generate(cookies: Cookies): Promise<string> {\n const result = await crypto.random.bytes(tokenLength)\n if (!result.success) {\n throw new Error(kitM('kit_csrfTokenFailed'))\n }\n\n const token = Array.from(result.data!)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n\n cookies.set(cookieName, token, {\n path: '/',\n httpOnly: true,\n secure: true,\n sameSite: 'strict',\n ...cookieOptions,\n })\n\n return token\n },\n\n /**\n * 验证 CSRF Token\n */\n async verify(event: RequestEvent): Promise<boolean> {\n const cookieToken = event.cookies.get(cookieName)\n if (!cookieToken) {\n return false\n }\n\n // 从 header 或 form data 获取 token\n let requestToken = event.request.headers.get(headerName)\n\n if (!requestToken && event.request.method === 'POST') {\n try {\n const formData = await event.request.clone().formData()\n const fieldValue = formData.get(formFieldName)\n requestToken = typeof fieldValue === 'string' ? fieldValue : null\n }\n catch {\n // 不是 form data\n }\n }\n\n if (!requestToken) {\n return false\n }\n\n // 时间安全的比较\n const result = await crypto.hash.timingSafeEqual(cookieToken, requestToken)\n return result.success && result.data === true\n },\n\n /**\n * 创建 CSRF 验证中间件\n */\n createHandle() {\n return async ({ event, resolve }: { event: RequestEvent, resolve: (event: RequestEvent) => Promise<Response> }) => {\n // 跳过安全方法\n const safeMethods = ['GET', 'HEAD', 'OPTIONS']\n if (safeMethods.includes(event.request.method)) {\n return resolve(event)\n }\n\n const isValid = await this.verify(event)\n if (!isValid) {\n return new Response(kitM('kit_csrfVerifyFailed'), { status: 403 })\n }\n\n return resolve(event)\n }\n },\n }\n}\n\n/**\n * 创建加密 Cookie 管理器\n *\n * 使用 AES 对 Cookie 值进行加解密,防止客户端篡改。\n *\n * @param config - 加密 Cookie 配置(含加密密钥)\n * @returns `{ set, get, delete }`\n *\n * @example\n * ```ts\n * const enc = createEncryptedCookie({ crypto, encryptionKey: 'my-32byte-key' })\n * await enc.set(cookies, 'prefs', { theme: 'dark' })\n * const prefs = await enc.get(cookies, 'prefs')\n * ```\n */\nexport function createEncryptedCookie(config: EncryptedCookieConfig) {\n const { crypto, encryptionKey, cookieOptions = {} } = config\n\n return {\n /**\n * 设置加密 Cookie\n */\n async set(cookies: Cookies, name: string, value: unknown): Promise<void> {\n const json = JSON.stringify(value)\n const result = await crypto.aes.encrypt(json, encryptionKey)\n\n if (!result.success) {\n throw new Error(kitM('kit_encryptFailed'))\n }\n\n cookies.set(name, result.data!, {\n path: '/',\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n ...cookieOptions,\n })\n },\n\n /**\n * 获取并解密 Cookie\n */\n async get<T = unknown>(cookies: Cookies, name: string): Promise<T | null> {\n const encrypted = cookies.get(name)\n if (!encrypted) {\n return null\n }\n\n try {\n const result = await crypto.aes.decrypt(encrypted, encryptionKey)\n if (!result.success) {\n return null\n }\n\n return JSON.parse(result.data!) as T\n }\n catch {\n return null\n }\n },\n\n /**\n * 删除 Cookie\n */\n delete(cookies: Cookies, name: string): void {\n cookies.delete(name, { path: '/' })\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/crypto/kit-crypto-helpers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @h-ai/kit — Crypto Helpers\n *\n * 集成 @h-ai/crypto 的 SvelteKit 工具\n * @module kit-crypto-helpers\n */\n\nimport type { Cookies, RequestEvent } from '@sveltejs/kit'\nimport type {\n CryptoCsrfConfig,\n CryptoServiceLike,\n EncryptedCookieConfig,\n WebhookVerifyConfig,\n} from './kit-crypto-types.js'\nimport { kitM } from '../../kit-i18n.js'\n\n/**\n * 验证 Webhook 签名\n *\n * 从请求头取出签名,用 HMAC 对原始 Body 进行比较验证。\n *\n * @param config - 签名验证配置\n * @returns 签名是否合法\n *\n * @example\n * ```ts\n * const valid = await verifyWebhookSignature({\n * crypto, event, secretKey: 'wh_secret', signatureHeader: 'X-Signature',\n * })\n * if (!valid) return new Response('Invalid signature', { status: 401 })\n * ```\n */\nexport async function verifyWebhookSignature(config: WebhookVerifyConfig): Promise<boolean> {\n const {\n crypto,\n event,\n secretKey,\n signatureHeader = 'X-Signature',\n algorithm = 'sha256',\n } = config\n\n const signature = event.request.headers.get(signatureHeader)\n if (!signature) {\n return false\n }\n\n const body = await event.request.text()\n\n try {\n const result = await crypto.hmac.verify(body, secretKey, signature, algorithm)\n return result.success && result.data === true\n }\n catch {\n return false\n }\n}\n\n/**\n * 生成请求 HMAC 签名(用于调用外部服务)\n *\n * @param crypto - @h-ai/crypto 服务实例\n * @param body - 请求体字符串\n * @param secretKey - 共享密钥\n * @param algorithm - 哈希算法(默认 `'sha256'`)\n * @returns 签名字符串\n * @throws 签名失败时抛出 Error\n */\nexport async function signRequest(\n crypto: CryptoServiceLike,\n body: string,\n secretKey: string,\n algorithm = 'sha256',\n): Promise<string> {\n const result = await crypto.hmac.sign(body, secretKey, algorithm)\n if (!result.success) {\n throw new Error(result.error?.message || kitM('kit_signFailed'))\n }\n return result.data!\n}\n\n/**\n * 创建 CSRF Token 管理器\n *\n * 基于 `@h-ai/crypto` 随机字节 + 时间安全比较,\n * 提供 token 生成、验证和 SvelteKit handle 中间件。\n *\n * @param config - CSRF 配置(crypto 实例、Cookie 名、Header 名等)\n * @returns `{ generate, verify, createHandle }`\n *\n * @example\n * ```ts\n * const csrf = createCsrfManager({ crypto })\n * // 在 load 中生成\n * const token = await csrf.generate(cookies)\n * // 在 action 中验证\n * const ok = await csrf.verify(event)\n * ```\n */\nexport function createCsrfManager(config: CryptoCsrfConfig) {\n const {\n crypto,\n cookieName = 'csrf_token',\n headerName = 'X-CSRF-Token',\n formFieldName = '_csrf',\n tokenLength = 32,\n cookieOptions = {},\n } = config\n\n return {\n /**\n * 生成新的 CSRF Token 并设置 Cookie\n */\n async generate(cookies: Cookies): Promise<string> {\n const result = await crypto.random.bytes(tokenLength)\n if (!result.success) {\n throw new Error(kitM('kit_csrfTokenFailed'))\n }\n\n const token = Array.from(result.data!)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n\n cookies.set(cookieName, token, {\n path: '/',\n httpOnly: true,\n secure: true,\n sameSite: 'strict',\n ...cookieOptions,\n })\n\n return token\n },\n\n /**\n * 验证 CSRF Token\n */\n async verify(event: RequestEvent): Promise<boolean> {\n const cookieToken = event.cookies.get(cookieName)\n if (!cookieToken) {\n return false\n }\n\n // 从 header 或 form data 获取 token\n let requestToken = event.request.headers.get(headerName)\n\n if (!requestToken && event.request.method === 'POST') {\n try {\n const formData = await event.request.clone().formData()\n const fieldValue = formData.get(formFieldName)\n requestToken = typeof fieldValue === 'string' ? fieldValue : null\n }\n catch {\n // 不是 form data\n }\n }\n\n if (!requestToken) {\n return false\n }\n\n // 时间安全的比较\n const result = await crypto.hash.timingSafeEqual(cookieToken, requestToken)\n return result.success && result.data === true\n },\n\n /**\n * 创建 CSRF 验证中间件\n */\n createHandle() {\n return async ({ event, resolve }: { event: RequestEvent, resolve: (event: RequestEvent) => Promise<Response> }) => {\n // 跳过安全方法\n const safeMethods = ['GET', 'HEAD', 'OPTIONS']\n if (safeMethods.includes(event.request.method)) {\n return resolve(event)\n }\n\n const isValid = await this.verify(event)\n if (!isValid) {\n return new Response(kitM('kit_csrfVerifyFailed'), { status: 403 })\n }\n\n return resolve(event)\n }\n },\n }\n}\n\n/**\n * 创建加密 Cookie 管理器\n *\n * 使用 AES 对 Cookie 值进行加解密,防止客户端篡改。\n *\n * @param config - 加密 Cookie 配置(含加密密钥)\n * @returns `{ set, get, delete }`\n *\n * @example\n * ```ts\n * const enc = createEncryptedCookie({ crypto, encryptionKey: 'my-32byte-key' })\n * await enc.set(cookies, 'prefs', { theme: 'dark' })\n * const prefs = await enc.get(cookies, 'prefs')\n * ```\n */\nexport function createEncryptedCookie(config: EncryptedCookieConfig) {\n const { crypto, encryptionKey, cookieOptions = {} } = config\n\n return {\n /**\n * 设置加密 Cookie\n */\n async set(cookies: Cookies, name: string, value: unknown): Promise<void> {\n const json = JSON.stringify(value)\n const result = await crypto.aes.encrypt(json, encryptionKey)\n\n if (!result.success) {\n throw new Error(kitM('kit_encryptFailed'))\n }\n\n cookies.set(name, result.data!, {\n path: '/',\n httpOnly: true,\n secure: true,\n sameSite: 'lax',\n ...cookieOptions,\n })\n },\n\n /**\n * 获取并解密 Cookie\n */\n async get<T = unknown>(cookies: Cookies, name: string): Promise<T | null> {\n const encrypted = cookies.get(name)\n if (!encrypted) {\n return null\n }\n\n try {\n const result = await crypto.aes.decrypt(encrypted, encryptionKey)\n if (!result.success) {\n return null\n }\n\n return JSON.parse(result.data!) as T\n }\n catch {\n return null\n }\n },\n\n /**\n * 删除 Cookie\n */\n delete(cookies: Cookies, name: string): void {\n cookies.delete(name, { path: '/' })\n },\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.55",
|
|
5
5
|
"description": "Hai Framework SvelteKit integration module.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"exports": {
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
"publishConfig": {
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=22.12.0"
|
|
45
|
+
},
|
|
43
46
|
"peerDependencies": {
|
|
44
47
|
"@sveltejs/adapter-node": "^5.5.4",
|
|
45
48
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
@@ -56,9 +59,9 @@
|
|
|
56
59
|
},
|
|
57
60
|
"dependencies": {
|
|
58
61
|
"zod": "^4.4.3",
|
|
59
|
-
"@h-ai/core": "0.1.0-alpha.
|
|
60
|
-
"@h-ai/crypto": "0.1.0-alpha.
|
|
61
|
-
"@h-ai/iam": "0.1.0-alpha.
|
|
62
|
+
"@h-ai/core": "0.1.0-alpha.55",
|
|
63
|
+
"@h-ai/crypto": "0.1.0-alpha.55",
|
|
64
|
+
"@h-ai/iam": "0.1.0-alpha.55"
|
|
62
65
|
},
|
|
63
66
|
"devDependencies": {
|
|
64
67
|
"@sveltejs/kit": "^2.61.1",
|
|
@@ -77,6 +80,7 @@
|
|
|
77
80
|
"typecheck": "tsc --noEmit",
|
|
78
81
|
"lint": "eslint .",
|
|
79
82
|
"lint:fix": "eslint . --fix",
|
|
80
|
-
"clean": "rimraf dist .turbo"
|
|
83
|
+
"clean": "rimraf dist .turbo",
|
|
84
|
+
"build:clean": "rimraf dist"
|
|
81
85
|
}
|
|
82
86
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/modules/crypto/kit-transport-middleware.ts"],"names":[],"mappings":";;;AAcA,IAAM,kBAAA,GAAqB,OAAA;AAC3B,IAAM,kCAAA,GAAqC,CAAC,kBAAA,EAAoB,qBAAqB,CAAA;AAmB9E,SAAS,8BAA8B,MAAA,EAA+C;AAE3F,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,OAAO,OAAO,QAAA,EAAU,IAAA,KAAS,IAAA,EAAK;AAAA,EACxC;AAEA,EAAA,MAAM,eAAA,GAAkB,OAAO,eAAA,IAAmB,mCAAA;AAClD,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,YAAA,IAAgB,EAAC;AAC7C,EAAA,MAAM,eAAA,GAAkB,OAAO,eAAA,IAAmB,IAAA;AAClD,EAAA,MAAM,iBAAA,GAAoB,OAAO,iBAAA,IAAqB,IAAA;AAGtD,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,SAAA,CAAU,YAAA,CAAa;AAAA,IAClD,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,YAAY,MAAA,CAAO;AAAA,GACpB,CAAA;AACD,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AAEnB,IAAA,OAAO,oCAAA,CAAqC,iBAAiB,YAAY,CAAA;AAAA,EAC3E;AACA,EAAA,MAAM,UAAU,MAAA,CAAO,IAAA;AAEvB,EAAA,OAAO,OAAO,SAAS,IAAA,KAAS;AAC9B,IAAA,MAAM,EAAE,OAAM,GAAI,OAAA;AAClB,IAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,KAAA,CAAM,OAAO,CAAA;AAGjD,IAAA,IAAI,QAAA,KAAa,eAAA,IAAmB,KAAA,CAAM,OAAA,CAAQ,WAAW,MAAA,EAAQ;AACnE,MAAA,OAAO,iBAAA,CAAkB,OAAA,EAAS,KAAA,CAAM,OAAO,CAAA;AAAA,IACjD;AAIA,IAAA,IAAI,CAAC,yBAAA,CAA0B,QAAA,EAAU,eAAe,CAAA,EAAG;AACzD,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AAGA,IAAA,IAAI,0BAAA,CAA2B,QAAA,EAAU,YAAA,EAAc,eAAe,CAAA,EAAG;AACvE,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AAGA,IAAA,MAAM,WAAW,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,mBAAmB,gBAAgB,CAAA;AAC9E,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,IAAI,iBAAA,EAAmB;AAErB,QAAA,OAAO,SAAA,CAAU,IAAA,CAAK,+BAA+B,CAAA,EAAG,GAAG,CAAA;AAAA,MAC7D;AAEA,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AAGA,IAAA,MAAM,eAAA,GAAkB,MAAM,OAAA,CAAQ,kBAAA,CAAmB,QAAQ,CAAA;AACjE,IAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,MAAA,OAAO,SAAA,CAAU,IAAA,CAAK,gCAAgC,CAAA,EAAG,GAAG,CAAA;AAAA,IAC9D;AAGA,IAAA,IAAI,OAAA,CAAQ,MAAM,OAAA,CAAQ,MAAM,KAAK,CAAC,kBAAA,CAAmB,KAAA,CAAM,OAAO,CAAA,EAAG;AACvE,MAAA,MAAM,aAAA,GAAgB,MAAM,kBAAA,CAAmB,KAAA,EAAO,SAAS,iBAAiB,CAAA;AAChF,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,OAAO,aAAA;AAAA,MACT;AAAA,IACF;AAGA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,EAAK;AAG5B,IAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,MAAA,OAAO,QAAA;AAAA,IACT;AAGA,IAAA,IAAI,eAAA,CAAgB,QAAQ,CAAA,EAAG;AAC7B,MAAA,OAAO,QAAA;AAAA,IACT;AAEA,IAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA;AAC5D,IAAA,IAAI,CAAC,6BAAA,CAA8B,WAAW,CAAA,EAAG;AAC/C,MAAA,OAAO,SAAA,CAAU,IAAA,CAAK,4BAA4B,CAAA,EAAG,GAAG,CAAA;AAAA,IAC1D;AACA,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA;AAC3D,IAAA,IAAI,iBAAiB,MAAA,CAAO,QAAA,CAAS,aAAA,EAAe,EAAE,IAAI,kBAAA,EAAoB;AAC5E,MAAA,OAAO,SAAA,CAAU,IAAA,CAAK,4BAA4B,CAAA,EAAG,GAAG,CAAA;AAAA,IAC1D;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,YAAA,GAAe,MAAM,QAAA,CAAS,KAAA,GAAQ,IAAA,EAAK;AACjD,MAAA,IAAI,CAAC,YAAA,EAAc;AACjB,QAAA,OAAO,QAAA;AAAA,MACT;AAEA,MAAA,MAAM,gBAAA,GAAmB,MAAM,OAAA,CAAQ,eAAA,CAAgB,UAAU,YAAY,CAAA;AAC7E,MAAA,IAAI,CAAC,iBAAiB,OAAA,EAAS;AAC7B,QAAA,OAAO,SAAA,CAAU,IAAA,CAAK,4BAA4B,CAAA,EAAG,GAAG,CAAA;AAAA,MAC1D;AACA,MAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAC5C,MAAA,OAAA,CAAQ,GAAA,CAAI,kBAAA,CAAmB,gBAAA,EAAkB,kBAAA,CAAmB,sBAAsB,CAAA;AAC1F,MAAA,OAAA,CAAQ,OAAO,gBAAgB,CAAA;AAC/B,MAAA,OAAO,IAAI,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,gBAAA,CAAiB,IAAI,CAAA,EAAG;AAAA,QACzD,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,YAAY,QAAA,CAAS,UAAA;AAAA,QACrB;AAAA,OACD,CAAA;AAAA,IACH,CAAA,CAAA,MACM;AACJ,MAAA,OAAO,SAAA,CAAU,IAAA,CAAK,4BAA4B,CAAA,EAAG,GAAG,CAAA;AAAA,IAC1D;AAAA,EACF,CAAA;AACF;AAEA,SAAS,8BAA8B,WAAA,EAA8B;AACnE,EAAA,OAAO,mCAAmC,IAAA,CAAK,CAAA,IAAA,KAAQ,WAAA,CAAY,QAAA,CAAS,IAAI,CAAC,CAAA;AACnF;AAEA,SAAS,oCAAA,CACP,iBACA,YAAA,EACY;AACZ,EAAA,OAAO,OAAO,SAAS,IAAA,KAAS;AAC9B,IAAA,MAAM,QAAA,GAAW,kBAAA,CAAmB,OAAA,CAAQ,KAAA,CAAM,OAAO,CAAA;AACzD,IAAA,IAAI,aAAa,eAAA,EAAiB;AAChC,MAAA,OAAO,SAAA,CAAU,IAAA,CAAK,kCAAkC,CAAA,EAAG,GAAG,CAAA;AAAA,IAChE;AACA,IAAA,IAAI,CAAC,yBAAA,CAA0B,QAAA,EAAU,eAAe,CAAA,EAAG;AACzD,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AACA,IAAA,IAAI,0BAAA,CAA2B,QAAA,EAAU,YAAA,EAAc,eAAe,CAAA,EAAG;AACvE,MAAA,OAAO,IAAA,EAAK;AAAA,IACd;AACA,IAAA,OAAO,SAAA,CAAU,IAAA,CAAK,kCAAkC,CAAA,EAAG,GAAG,CAAA;AAAA,EAChE,CAAA;AACF;AAEA,SAAS,gBAAgB,QAAA,EAA6B;AACpD,EAAA,IAAI,CAAC,GAAA,EAAK,GAAG,CAAA,CAAE,QAAA,CAAS,SAAS,MAAM,CAAA;AACrC,IAAA,OAAO,IAAA;AACT,EAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,KAAM,GAAA;AACpD;AAEA,SAAS,mBAAmB,OAAA,EAA0B;AACpD,EAAA,OAAO,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,QAAA;AAC9B;AAEA,SAAS,SAAA,CAAU,SAAiB,MAAA,EAA0B;AAC5D,EAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG;AAAA,IACtD,MAAA;AAAA,IACA,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB,GAC/C,CAAA;AACH;AAWA,eAAe,iBAAA,CACb,SACA,OAAA,EACmB;AACnB,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,IAAA,EAAK;AAEhC,IAAA,IAAI,CAAC,IAAA,CAAK,eAAA,IAAmB,OAAO,IAAA,CAAK,oBAAoB,QAAA,EAAU;AACrE,MAAA,OAAO,SAAA,CAAU,IAAA,CAAK,6BAA6B,CAAA,EAAG,GAAG,CAAA;AAAA,IAC3D;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,iBAAA,CAAkB,KAAK,eAAe,CAAA;AACrE,IAAA,MAAM,eAAA,GAAkB,QAAQ,kBAAA,EAAmB;AAEnD,IAAA,OAAO,IAAI,QAAA;AAAA,MACT,IAAA,CAAK,SAAA,CAAU,EAAE,eAAA,EAAiB,UAAU,CAAA;AAAA,MAC5C,EAAE,MAAA,EAAQ,GAAA,EAAK,SAAS,EAAE,cAAA,EAAgB,oBAAmB;AAAE,KACjE;AAAA,EACF,CAAA,CAAA,MACM;AACJ,IAAA,OAAO,SAAA,CAAU,IAAA,CAAK,gCAAgC,CAAA,EAAG,GAAG,CAAA;AAAA,EAC9D;AACF;AAQA,SAAS,QAAQ,MAAA,EAAyB;AACxC,EAAA,OAAO,CAAC,QAAQ,KAAA,EAAO,OAAA,EAAS,QAAQ,CAAA,CAAE,QAAA,CAAS,MAAA,CAAO,WAAA,EAAa,CAAA;AACzE;AAGA,SAAS,mBAAmB,OAAA,EAA2B;AACrD,EAAA,IAAI,QAAQ,IAAA,KAAS,IAAA;AACnB,IAAA,OAAO,IAAA;AAET,EAAA,MAAM,aAAA,GAAgB,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA;AAC1D,EAAA,IAAI,aAAA,KAAkB,IAAA;AACpB,IAAA,OAAO,MAAA,CAAO,QAAA,CAAS,aAAA,EAAe,EAAE,CAAA,KAAM,CAAA;AAEhD,EAAA,OAAO,KAAA;AACT;AAeA,SAAS,kBAAA,CAAmB,OAAsB,IAAA,EAAoB;AACpE,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,KAAA,CAAM,QAAQ,OAAO,CAAA;AACjD,EAAA,OAAA,CAAQ,MAAA,CAAO,mBAAmB,gBAAgB,CAAA;AAClD,EAAA,OAAA,CAAQ,OAAO,gBAAgB,CAAA;AAC/B,EAAA,OAAA,CAAQ,GAAA,CAAI,gBAAgB,iCAAiC,CAAA;AAC7D,EAAA,MAAM,UAAA,GAAa,IAAI,OAAA,CAAQ,KAAA,CAAM,QAAQ,GAAA,EAAK;AAAA,IAChD,MAAA,EAAQ,MAAM,OAAA,CAAQ,MAAA;AAAA,IACtB,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AACD,EAAA,MAAA,CAAO,cAAA,CAAe,OAAO,SAAA,EAAW;AAAA,IACtC,KAAA,EAAO,UAAA;AAAA,IACP,QAAA,EAAU,IAAA;AAAA,IACV,YAAA,EAAc;AAAA,GACf,CAAA;AACH;AAcA,eAAe,kBAAA,CACb,KAAA,EACA,OAAA,EACA,iBAAA,EAC0B;AAC1B,EAAA,MAAM,WAAA,GAAc,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,kBAAA,CAAmB,gBAAgB,MAC3E,kBAAA,CAAmB,sBAAA;AACzB,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,IAAI,CAAC,iBAAA;AACH,MAAA,OAAO,IAAA;AACT,IAAA,OAAO,SAAA,CAAU,IAAA,CAAK,6BAA6B,CAAA,EAAG,GAAG,CAAA;AAAA,EAC3D;AAEA,EAAA,MAAM,cAAc,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA;AACjE,EAAA,IAAI,CAAC,WAAA,CAAY,QAAA,CAAS,kBAAkB,CAAA,EAAG;AAC7C,IAAA,IAAI,CAAC,iBAAA,EAAmB;AACtB,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,SAAA,CAAU,IAAA,CAAK,6BAA6B,CAAA,EAAG,GAAG,CAAA;AAAA,EAC3D;AAEA,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAK;AAC1C,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,IAAA,CAAK,MAAM,QAAQ,CAAA;AAAA,EAC/B,CAAA,CAAA,MACM;AACJ,IAAA,IAAI,CAAC,iBAAA,EAAmB;AACtB,MAAA,kBAAA,CAAmB,OAAO,QAAQ,CAAA;AAClC,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,SAAA,CAAU,IAAA,CAAK,4BAA4B,CAAA,EAAG,GAAG,CAAA;AAAA,EAC1D;AAEA,EAAA,IAAI,CAAC,uBAAA,CAAwB,OAAO,CAAA,EAAG;AACrC,IAAA,IAAI,CAAC,iBAAA,EAAmB;AACtB,MAAA,kBAAA,CAAmB,OAAO,QAAQ,CAAA;AAClC,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAO,SAAA,CAAU,IAAA,CAAK,6BAA6B,CAAA,EAAG,GAAG,CAAA;AAAA,EAC3D;AAEA,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,cAAA,CAAe,OAAO,CAAA;AAChD,EAAA,IAAI,CAAC,UAAU,OAAA,EAAS;AACtB,IAAA,OAAO,SAAA,CAAU,IAAA,CAAK,4BAA4B,CAAA,EAAG,GAAG,CAAA;AAAA,EAC1D;AACA,EAAA,kBAAA,CAAmB,KAAA,EAAO,UAAU,IAAI,CAAA;AACxC,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,wBAAwB,OAAA,EAA+C;AAC9E,EAAA,IAAI,CAAC,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA;AACjC,IAAA,OAAO,KAAA;AACT,EAAA,MAAM,CAAA,GAAI,OAAA;AACV,EAAA,OAAO,OAAO,CAAA,CAAE,YAAA,KAAiB,QAAA,IAAY,OAAO,EAAE,UAAA,KAAe,QAAA,IAAY,OAAO,CAAA,CAAE,EAAA,KAAO,QAAA;AACnG","file":"chunk-2XRMSVLR.js","sourcesContent":["/**\n * @h-ai/kit — 传输加密中间件\n *\n * SvelteKit 中间件,自动对请求/响应进行传输加密解密。\n * @module kit-transport-middleware\n */\n\nimport type { EncryptedPayload, TransportEncryptionManager } from '@h-ai/crypto'\nimport type { Middleware } from '../../kit-types.js'\nimport type { TransportEncryptionConfig } from './kit-crypto-types.js'\nimport { 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 MAX_ENCRYPTED_BODY = 1_048_576\nconst ENCRYPTABLE_RESPONSE_CONTENT_TYPES = ['application/json', 'text/sveltekit-data']\n\n/**\n * 创建传输加密中间件\n *\n * @param config - 传输加密配置\n * @returns SvelteKit 中间件\n *\n * @example\n * ```ts\n * import { crypto } from '@h-ai/crypto'\n * import { kit } from '@h-ai/kit'\n *\n * // 传输加密通过 createHandle 的 crypto 配置自动启用:\n * export const handle = kit.createHandle({\n * crypto: { crypto, transport: true },\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 const encryptResponse = config.encryptResponse ?? true\n const requireEncryption = config.requireEncryption ?? true\n\n // 初始化传输加密管理器\n const result = config.crypto.transport.createServer({\n keyStore: config.keyStore,\n maxClients: config.maxClients,\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 if (requireEncryption) {\n // 强制加密模式:缺少 X-Client-Id 说明未完成密钥交换,拒绝请求\n return jsonError(kitM('kit_transportClientIdRequired'), 400)\n }\n // 非强制模式:透传明文(渐进式迁移兼容)\n return next()\n }\n\n // 检查客户端是否已注册\n const clientPublicKey = await manager.getClientPublicKey(clientId)\n if (!clientPublicKey) {\n return jsonError(kitM('kit_transportClientKeyNotFound'), 400)\n }\n\n // ── 解密请求 ──\n if (hasBody(event.request.method) && !isRequestBodyEmpty(event.request)) {\n const decryptResult = await decryptRequestBody(event, manager, requireEncryption)\n if (decryptResult) {\n return decryptResult\n }\n }\n\n // ── 执行后续中间件/端点 ──\n const response = await next()\n\n // ── 加密响应 ──\n if (!encryptResponse) {\n return response\n }\n\n // 只允许可安全加密的响应体离开受保护路由;无法加密时 fail-closed,禁止明文泄露。\n if (isEmptyResponse(response)) {\n return response\n }\n\n const contentType = response.headers.get('Content-Type') ?? ''\n if (!canEncryptResponseContentType(contentType)) {\n return jsonError(kitM('kit_transportEncryptFailed'), 500)\n }\n const contentLength = response.headers.get('Content-Length')\n if (contentLength && Number.parseInt(contentLength, 10) > MAX_ENCRYPTED_BODY) {\n return jsonError(kitM('kit_transportEncryptFailed'), 500)\n }\n\n try {\n const responseBody = await response.clone().text()\n if (!responseBody) {\n return response\n }\n\n const encryptedPayload = await manager.encryptResponse(clientId, responseBody)\n if (!encryptedPayload.success) {\n return jsonError(kitM('kit_transportEncryptFailed'), 500)\n }\n const headers = new Headers(response.headers)\n headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE)\n headers.delete('Content-Length')\n return new Response(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 return response.headers.get('Content-Length') === '0'\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 try {\n const body = await request.json() as { clientPublicKey?: string }\n\n if (!body.clientPublicKey || typeof body.clientPublicKey !== 'string') {\n return jsonError(kitM('kit_transportInvalidPayload'), 400)\n }\n\n const clientId = await manager.registerClientKey(body.clientPublicKey)\n const serverPublicKey = manager.getServerPublicKey()\n\n return new Response(\n JSON.stringify({ 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 * 判断 HTTP 方法是否可能携带请求体\n *\n * @param method - HTTP 方法字符串\n * @returns `true` 表示 POST / PUT / PATCH / DELETE\n */\nfunction hasBody(method: string): boolean {\n return ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method.toUpperCase())\n}\n\n/** 判断请求是否真实携带 body,避免空 POST 被误判为非法密文。 */\nfunction isRequestBodyEmpty(request: Request): boolean {\n if (request.body === null)\n return true\n\n const contentLength = request.headers.get('Content-Length')\n if (contentLength !== null)\n return Number.parseInt(contentLength, 10) === 0\n\n return false\n}\n\ninterface RequestHolder {\n request: Request\n}\n\n/**\n * 重建 SvelteKit 事件中的 Request。\n *\n * `request.text()` 会消费 body;当请求不是加密载荷但允许明文透传时,必须把\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.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 })\n Object.defineProperty(event, 'request', {\n value: newRequest,\n writable: true,\n configurable: true,\n })\n}\n\n/**\n * 尝试解密请求体。\n *\n * 非 JSON 请求体(例如 multipart/form-data 文件上传)无法承载传输加密 JSON\n * 载荷;在非强制加密模式下应原样透传,不读取 body,避免破坏浏览器生成的\n * multipart 边界。\n *\n * @param event SvelteKit 请求事件\n * @param manager 传输加密管理器\n * @param requireEncryption 是否强制要求请求加密\n * @returns 需要提前返回的错误响应;返回 null 表示继续后续中间件\n */\nasync function decryptRequestBody(\n event: RequestHolder,\n manager: TransportEncryptionManager,\n requireEncryption: boolean,\n): Promise<Response | null> {\n const isEncrypted = event.request.headers.get(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER)\n === TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE\n if (!isEncrypted) {\n if (!requireEncryption)\n return null\n return jsonError(kitM('kit_transportInvalidPayload'), 400)\n }\n\n const contentType = event.request.headers.get('Content-Type') ?? ''\n if (!contentType.includes('application/json')) {\n if (!requireEncryption) {\n return null\n }\n return jsonError(kitM('kit_transportInvalidPayload'), 400)\n }\n\n const bodyText = await event.request.text()\n if (!bodyText) {\n return null\n }\n\n let payload: unknown\n try {\n payload = JSON.parse(bodyText) as unknown\n }\n catch {\n if (!requireEncryption) {\n replaceRequestBody(event, bodyText)\n return null\n }\n return jsonError(kitM('kit_transportDecryptFailed'), 400)\n }\n\n if (!isEncryptedPayloadShape(payload)) {\n if (!requireEncryption) {\n replaceRequestBody(event, bodyText)\n return null\n }\n return jsonError(kitM('kit_transportInvalidPayload'), 400)\n }\n\n const plaintext = manager.decryptRequest(payload)\n if (!plaintext.success) {\n return jsonError(kitM('kit_transportDecryptFailed'), 400)\n }\n replaceRequestBody(event, plaintext.data)\n return null\n}\n\nfunction isEncryptedPayloadShape(payload: unknown): payload is EncryptedPayload {\n if (!payload || typeof payload !== 'object')\n return false\n const p = payload as Record<string, unknown>\n return typeof p.encryptedKey === 'string' && typeof p.ciphertext === 'string' && typeof p.iv === 'string'\n}\n"]}
|
package/dist/chunk-475WECWK.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { iam } from '@h-ai/iam';
|
|
2
|
-
|
|
3
|
-
// src/modules/a2a/kit-a2a-auth.ts
|
|
4
|
-
function createA2AApiKeyAuthenticator(config) {
|
|
5
|
-
return async (event) => {
|
|
6
|
-
const rawKey = config.in === "header" ? event.request.headers.get(config.name) : event.url.searchParams.get(config.name);
|
|
7
|
-
if (!rawKey)
|
|
8
|
-
return null;
|
|
9
|
-
const result = await iam.apiKey.verifyApiKey(rawKey);
|
|
10
|
-
if (!result.success)
|
|
11
|
-
return null;
|
|
12
|
-
return {
|
|
13
|
-
agentId: result.data.userId,
|
|
14
|
-
apiKeyId: result.data.id,
|
|
15
|
-
scopes: result.data.scopes
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// src/modules/a2a/kit-a2a-handle.ts
|
|
21
|
-
function resolveA2AConfig(input) {
|
|
22
|
-
if (!input)
|
|
23
|
-
return null;
|
|
24
|
-
if ("getAgentCard" in input && "handleRequest" in input) {
|
|
25
|
-
return {
|
|
26
|
-
operations: input,
|
|
27
|
-
cardPath: "/.well-known/agent.json",
|
|
28
|
-
rpcPath: "/a2a"
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const cfg = input;
|
|
32
|
-
return {
|
|
33
|
-
operations: cfg.operations,
|
|
34
|
-
cardPath: cfg.cardPath ?? "/.well-known/agent.json",
|
|
35
|
-
rpcPath: cfg.rpcPath ?? "/a2a",
|
|
36
|
-
authenticate: resolveAuthenticate(cfg.authenticate, cfg.operations)
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function resolveAuthenticate(authenticate, operations) {
|
|
40
|
-
if (!authenticate)
|
|
41
|
-
return void 0;
|
|
42
|
-
if (typeof authenticate === "function")
|
|
43
|
-
return authenticate;
|
|
44
|
-
const cardResult = operations.getAgentCard();
|
|
45
|
-
const security = cardResult.success && cardResult.data ? cardResult.data.security : void 0;
|
|
46
|
-
const apiKeyCfg = security?.apiKey;
|
|
47
|
-
return createA2AApiKeyAuthenticator({
|
|
48
|
-
in: apiKeyCfg?.in ?? "header",
|
|
49
|
-
name: apiKeyCfg?.name ?? "x-api-key"
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
async function handleA2ARequest(event, requestId, config) {
|
|
53
|
-
const { pathname } = event.url;
|
|
54
|
-
if (pathname === config.cardPath && event.request.method === "GET") {
|
|
55
|
-
const result = config.operations.getAgentCard();
|
|
56
|
-
if (result.success) {
|
|
57
|
-
return new Response(JSON.stringify(result.data), {
|
|
58
|
-
headers: { "Content-Type": "application/json", "X-Request-Id": requestId }
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
return new Response(
|
|
62
|
-
JSON.stringify({ error: { code: "A2A_NOT_CONFIGURED", message: "Agent card not available" } }),
|
|
63
|
-
{ status: 503, headers: { "Content-Type": "application/json", "X-Request-Id": requestId } }
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
if (pathname === config.rpcPath && event.request.method === "POST") {
|
|
67
|
-
let context;
|
|
68
|
-
if (config.authenticate) {
|
|
69
|
-
const authResult = await config.authenticate(event);
|
|
70
|
-
if (authResult) {
|
|
71
|
-
context = authResult;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const body = await event.request.json();
|
|
75
|
-
const result = await config.operations.handleRequest(body, context);
|
|
76
|
-
if (result.streaming && result.stream) {
|
|
77
|
-
const stream = result.stream;
|
|
78
|
-
const readable = new ReadableStream({
|
|
79
|
-
async start(controller) {
|
|
80
|
-
const encoder = new TextEncoder();
|
|
81
|
-
try {
|
|
82
|
-
for await (const chunk of stream) {
|
|
83
|
-
controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
|
|
84
|
-
|
|
85
|
-
`));
|
|
86
|
-
}
|
|
87
|
-
} finally {
|
|
88
|
-
controller.close();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
return new Response(readable, {
|
|
93
|
-
headers: {
|
|
94
|
-
"Content-Type": "text/event-stream",
|
|
95
|
-
"Cache-Control": "no-cache",
|
|
96
|
-
"Connection": "keep-alive",
|
|
97
|
-
"X-Request-Id": requestId
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
return new Response(JSON.stringify(result.body), {
|
|
102
|
-
headers: { "Content-Type": "application/json", "X-Request-Id": requestId }
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export { createA2AApiKeyAuthenticator, handleA2ARequest, resolveA2AConfig };
|
|
109
|
-
//# sourceMappingURL=chunk-475WECWK.js.map
|
|
110
|
-
//# sourceMappingURL=chunk-475WECWK.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/modules/a2a/kit-a2a-auth.ts","../src/modules/a2a/kit-a2a-handle.ts"],"names":[],"mappings":"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","file":"chunk-475WECWK.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/** A2A API Key 认证器配置 */\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 */\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 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 { createA2AApiKeyAuthenticator } from './kit-a2a-auth.js'\n\n// ─── 解析后的 A2A 内部配置 ───\n\n/** A2A 解析后的内部配置 */\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 */\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.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.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 security = cardResult.success && cardResult.data\n ? (cardResult.data as Record<string, unknown>).security as { apiKey?: { in: 'header' | 'query', name: string } } | undefined\n : undefined\n const apiKeyCfg = security?.apiKey\n\n return createA2AApiKeyAuthenticator({\n in: apiKeyCfg?.in ?? '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 */\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: 'Agent card not available' } }),\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 // 可选认证\n let context: Record<string, unknown> | undefined\n if (config.authenticate) {\n const authResult = await config.authenticate(event)\n if (authResult) {\n context = authResult\n }\n }\n\n const body = await event.request.json()\n const result = await config.operations.handleRequest(body, context)\n\n if (result.streaming && result.stream) {\n const stream = result.stream\n const readable = new ReadableStream({\n async start(controller) {\n const encoder = new TextEncoder()\n try {\n for await (const chunk of stream) {\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`))\n }\n }\n finally {\n controller.close()\n }\n },\n })\n return new Response(readable, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n 'X-Request-Id': requestId,\n },\n })\n }\n\n return new Response(JSON.stringify(result.body), {\n headers: { 'Content-Type': 'application/json', 'X-Request-Id': requestId },\n })\n }\n\n return null\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/kit-auth.ts","../src/client/kit-client.ts"],"names":[],"mappings":"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","file":"chunk-DXZYOYXZ.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 } 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 */\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\ninterface CookieReader {\n get: (name: string) => string | undefined\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/** 浏览器 Token 存储器 */\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/** 获取默认浏览器 Token 存储(页面内存,不持久化)。 */\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 */\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 secure: process.env.NODE_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 *\n * @example\n * ```ts\n * const result = await kit.auth.login(cookies, { identifier, password })\n * if (!result.success) return kit.response.fromError(result.error, IamErrorHttpStatus)\n * const { user, roles, permissions } = result.data\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 *\n * @example\n * ```ts\n * const result = await kit.auth.loginWithOtp(cookies, { identifier, code })\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 *\n * @example\n * ```ts\n * const result = await kit.auth.loginWithLdap(cookies, { username, password })\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 *\n * @example\n * ```ts\n * const result = await kit.auth.loginWithApiKey(cookies, { key: apiKey })\n * if (!result.success) return kit.response.fromError(result.error, IamErrorHttpStatus)\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 *\n * @example\n * ```ts\n * const result = await kit.auth.registerAndLogin(cookies, { username, email, password })\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 *\n * @param cookies - SvelteKit cookies 对象\n * @param accessToken - 访问令牌(为 null/undefined 时仅清除 Cookie)\n *\n * @example\n * ```ts\n * await kit.auth.logout(cookies, locals.accessToken)\n * return kit.response.ok(null)\n * ```\n */\nexport async function logout(\n cookies: CookieWriter,\n accessToken?: string | null,\n): Promise<void> {\n // 即使 auth 未配置也需要清除 Cookie,保证登出语义幂等\n const ops = getAuthOperations()\n if (accessToken && ops) {\n await ops.logout(accessToken)\n }\n clearToken(cookies)\n}\n\n// ─── 浏览器端 Token 存储(通过 kit.auth 暴露) ───\n\n/**\n * 创建浏览器端 Token 存储器(localStorage)。\n *\n * ⚠️ 安全提示:localStorage 对同源脚本可读,任意 XSS 都可能窃取 Token。\n * 本函数仅供调用方显式接受风险时使用;`kit.client.create({ auth: true })`\n * 不会默认调用它。\n *\n * @param key - localStorage 键名,默认 `'hai_access_token'`\n */\nexport function createTokenStore(key = DEFAULT_TOKEN_COOKIE_NAME): BrowserTokenStore {\n return {\n get(): string | null {\n if (typeof window === 'undefined') {\n return null\n }\n return window.localStorage.getItem(key)\n },\n set(token: string): void {\n if (typeof window === 'undefined') {\n return\n }\n window.localStorage.setItem(key, token)\n },\n clear(): void {\n if (typeof window === 'undefined') {\n return\n }\n window.localStorage.removeItem(key)\n },\n }\n}\n\n/**\n * 写入默认浏览器端 Access Token(仅页面内存,不持久化到 localStorage)。\n */\nexport function setBrowserToken(token: string): void {\n defaultBrowserTokenStore.set(token)\n}\n\n/**\n * 清除默认浏览器端 Access Token(仅页面内存)。\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 */\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 */\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 */\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\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 */\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 *\n * @example\n * ```ts\n * import { browser } from '$app/environment'\n * import { crypto } from '@h-ai/crypto'\n * import { kit } from '@h-ai/kit'\n * import { adminConsoleKitConfig } from '$lib/config/kit-config.js'\n *\n * if (browser) {\n * kit.client.installBrowserTransport(adminConsoleKitConfig, { 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 *\n * @example\n * ```ts\n * const client = createKitClient({ transport: { crypto } })\n * const res = await client.apiFetch('/api/users', { method: 'POST', body: '{}' })\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"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../messages/en-US.json","../messages/zh-CN.json","../src/kit-i18n.ts","../src/modules/crypto/kit-transport-paths.ts"],"names":[],"mappings":"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","file":"chunk-MI65L22O.js","sourcesContent":["{\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}\n","{\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}\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 messagesZhCN from '../messages/zh-CN.json'\n\ntype KitMessageKey = keyof typeof messagesZhCN\n\n/** 获取 Kit 模块的 i18n 消息 */\nexport const kitM\n = core.i18n.createMessageGetter<KitMessageKey>({ 'zh-CN': messagesZhCN, 'en-US': messagesEnUS })\n\n/**\n * 统一设置所有 hai 模块的默认语言\n *\n * 通过 @h-ai/core 的集中式 locale 管理器,一次调用即可同步所有模块。\n * 各模块的 createMessageGetter 会读取全局 locale。\n *\n * @example\n * ```ts\n * import { kit } from '@h-ai/kit'\n *\n * // 在 i18n handle 中\n * const locale = event.cookies.get('PARAGLIDE_LOCALE') ?? 'zh-CN'\n * kit.i18n.setLocale(locale)\n * ```\n */\nexport function setAllModulesLocale(locale: string): void {\n core.i18n.setGlobalLocale(locale)\n}\n","/**\n * @h-ai/kit — 传输加密路径匹配工具\n *\n * 统一定义哪些同源请求应纳入 transport 保护范围,供服务端 middleware\n * 与浏览器端 fetch 包装共同复用,避免两端判断漂移。\n * @module kit-transport-paths\n */\n\nimport { TRANSPORT_PROTOCOL } from '@h-ai/crypto'\n\nexport const DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH = `/api${TRANSPORT_PROTOCOL.DEFAULT_KEY_EXCHANGE_PATH}`\n\nconst API_PREFIX = '/api/'\nconst ROOT_DATA_PATH = '/__data.json'\nconst DATA_SUFFIX = '/__data.json'\n\nfunction normalizePath(pathname: string): string {\n return pathname.startsWith('/') ? pathname : `/${pathname}`\n}\n\n/** 判断路径是否为 SvelteKit 的页面数据端点。 */\nexport function isSvelteKitDataPath(pathname: string): boolean {\n return pathname === ROOT_DATA_PATH || pathname.endsWith(DATA_SUFFIX)\n}\n\n/** 判断路径是否属于 transport 保护范围。 */\nexport function shouldHandleTransportPath(\n pathname: string,\n keyExchangePath: string = DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH,\n): boolean {\n return pathname === keyExchangePath || pathname.startsWith(API_PREFIX) || isSvelteKitDataPath(pathname)\n}\n\n/** 判断路径是否应排除 transport。 */\nexport function shouldExcludeTransportPath(\n pathname: string,\n excludePaths: string[],\n keyExchangePath: string,\n): boolean {\n if (pathname === keyExchangePath)\n return true\n\n return excludePaths.some((excludePath) => {\n const normalizedPath = normalizePath(excludePath)\n return pathname === normalizedPath || pathname.startsWith(`${normalizedPath}/`)\n })\n}\n\n/** 从相对/绝对 URL 中提取 pathname。 */\nexport function resolveTransportPath(urlOrPath: string, origin = 'http://localhost'): string {\n return new URL(urlOrPath, origin).pathname\n}\n\n/**\n * 判断浏览器侧请求是否应走 transport。\n *\n * - 仅处理同源请求\n * - 仅保护 `/api/*` 与 SvelteKit `__data.json`\n * - 命中 `excludePaths` 时透传明文\n */\nexport function shouldUseTransportForUrl(\n url: string | URL,\n options: {\n origin?: string\n keyExchangePath?: string\n excludePaths?: string[]\n } = {},\n): boolean {\n const baseOrigin = options.origin ?? 'http://localhost'\n const requestUrl = url instanceof URL ? url : new URL(url, baseOrigin)\n\n if (options.origin && requestUrl.origin !== options.origin)\n return false\n\n const keyExchangePath = options.keyExchangePath ?? DEFAULT_TRANSPORT_KEY_EXCHANGE_PATH\n if (!shouldHandleTransportPath(requestUrl.pathname, keyExchangePath))\n return false\n\n return !shouldExcludeTransportPath(requestUrl.pathname, options.excludePaths ?? [], keyExchangePath)\n}\n"]}
|