@h-ai/api-client 0.1.0-alpha8 → 1.0.0-beta.1
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.en.md +210 -0
- package/README.ja.md +210 -0
- package/README.md +135 -265
- package/dist/index.d.ts +2758 -250
- package/dist/index.js +677 -632
- package/dist/index.js.map +1 -1
- package/package.json +19 -10
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api-client-auth.ts","../messages/en-US.json","../messages/zh-CN.json","../src/api-client-i18n.ts","../src/api-client-types.ts","../src/api-client-contract.ts","../src/api-client-error.ts","../src/api-client-fetch.ts","../src/api-client-token-manager.ts","../src/api-client-main.ts"],"names":["core","err","ok","logger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @h-ai/api-client — Token 存储适配器\n *\n * 提供 localStorage 和内存两种内置 Token 存储实现。\n * @module api-client-auth\n */\n\nimport type { TokenStorage } from './api-client-types.js'\n\n// ─── localStorage 存储 ───\n\nconst LS_ACCESS_KEY = 'hai_access_token'\nconst LS_REFRESH_KEY = 'hai_refresh_token'\n\n/**\n * 创建基于 localStorage 的 Token 存储\n *\n * 适用于浏览器端 SPA / PWA。Capacitor 环境建议使用\n * `@h-ai/capacitor` 提供的 `CapacitorTokenStorage`。\n *\n * @returns TokenStorage 实例\n *\n * @example\n * ```ts\n * import { api, createLocalStorageTokenStorage } from '@h-ai/api-client'\n *\n * await api.init({\n * baseUrl: 'https://api.example.com',\n * auth: {\n * storage: createLocalStorageTokenStorage(),\n * refreshUrl: '/auth/refresh',\n * },\n * })\n * ```\n */\nexport function createLocalStorageTokenStorage(): TokenStorage {\n return {\n async getAccessToken() {\n return globalThis.localStorage?.getItem(LS_ACCESS_KEY) ?? null\n },\n async setAccessToken(token: string) {\n globalThis.localStorage?.setItem(LS_ACCESS_KEY, token)\n },\n async getRefreshToken() {\n return globalThis.localStorage?.getItem(LS_REFRESH_KEY) ?? null\n },\n async setRefreshToken(token: string) {\n globalThis.localStorage?.setItem(LS_REFRESH_KEY, token)\n },\n async clear() {\n globalThis.localStorage?.removeItem(LS_ACCESS_KEY)\n globalThis.localStorage?.removeItem(LS_REFRESH_KEY)\n },\n }\n}\n\n// ─── 内存存储 ───\n\n/**\n * 创建内存 Token 存储\n *\n * 适用于 Node.js 测试、SSR 或短生命周期场景。\n * 页面刷新后 Token 丢失。\n *\n * @returns TokenStorage 实例\n *\n * @example\n * ```ts\n * import { api, createMemoryTokenStorage } from '@h-ai/api-client'\n *\n * await api.init({\n * baseUrl: 'https://api.example.com',\n * auth: {\n * storage: createMemoryTokenStorage(),\n * refreshUrl: '/auth/refresh',\n * },\n * })\n * ```\n */\nexport function createMemoryTokenStorage(): TokenStorage {\n let accessToken: string | null = null\n let refreshToken: string | null = null\n\n return {\n async getAccessToken() {\n return accessToken\n },\n async setAccessToken(token: string) {\n accessToken = token\n },\n async getRefreshToken() {\n return refreshToken\n },\n async setRefreshToken(token: string) {\n refreshToken = token\n },\n async clear() {\n accessToken = null\n refreshToken = null\n },\n }\n}\n","{\n \"apiClient_networkError\": \"Network request failed\",\n \"apiClient_timeout\": \"Request timeout\",\n \"apiClient_serverError\": \"Server error ({status})\",\n \"apiClient_unauthorized\": \"Unauthorized, please log in again\",\n \"apiClient_forbidden\": \"Access forbidden\",\n \"apiClient_notFound\": \"Resource not found\",\n \"apiClient_validationFailed\": \"Request validation failed\",\n \"apiClient_tokenRefreshFailed\": \"Token refresh failed, please log in again\",\n \"apiClient_notInitialized\": \"API client not initialized, please call api.init() first\",\n \"apiClient_configError\": \"API client config error: {error}\",\n \"apiClient_responseValidationFailed\": \"Response validation failed\",\n \"apiClient_unknown\": \"Unknown error\"\n}\n","{\n \"apiClient_networkError\": \"网络请求失败\",\n \"apiClient_timeout\": \"请求超时\",\n \"apiClient_serverError\": \"服务器错误({status})\",\n \"apiClient_unauthorized\": \"未授权,请重新登录\",\n \"apiClient_forbidden\": \"无权限访问\",\n \"apiClient_notFound\": \"请求的资源不存在\",\n \"apiClient_validationFailed\": \"请求参数校验失败\",\n \"apiClient_tokenRefreshFailed\": \"Token刷新失败,请重新登录\",\n \"apiClient_notInitialized\": \"API 客户端未初始化,请先调用 api.init()\",\n \"apiClient_configError\": \"API 客户端配置错误:{error}\",\n \"apiClient_responseValidationFailed\": \"响应数据校验失败\",\n \"apiClient_unknown\": \"未知错误\"\n}\n","/**\n * @h-ai/api-client — i18n\n *\n * 模块 i18n 文案访问入口。\n * @module api-client-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 ApiClientMessageKey = keyof typeof messagesZhCN\n\n/**\n * api-client 模块 i18n 文案获取器\n *\n * @param key - 文案 Key\n * @param options - 插值参数\n * @returns 本地化后的文案字符串\n */\nexport const apiClientM = core.i18n.createMessageGetter<ApiClientMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n})\n","/**\n * @h-ai/api-client — 类型定义\n *\n * 通用 HTTP 客户端的公共类型、EndpointDef 契约接口、\n * TokenStorage 适配器接口等。\n * @module api-client-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport type { z } from 'zod'\nimport { core } from '@h-ai/core'\n\n// ─── 错误定义(照 @h-ai/core 范式) ───\n\nconst ApiClientErrorInfo = {\n NETWORK_ERROR: '001:502',\n TIMEOUT: '002:504',\n SERVER_ERROR: '003:502',\n UNAUTHORIZED: '004:401',\n FORBIDDEN: '005:403',\n NOT_FOUND: '006:404',\n VALIDATION_FAILED: '007:400',\n TOKEN_REFRESH_FAILED: '008:401',\n NOT_INITIALIZED: '010:500',\n CONFIG_ERROR: '011:500',\n UNKNOWN: '099:500',\n} as const satisfies ErrorInfo\n\nexport const HaiApiClientError = core.error.buildHaiErrorsDef('api-client', ApiClientErrorInfo)\n\n// ─── API 契约 ───\n\n/**\n * API 端点契约定义\n *\n * 描述单个 API 端点的 HTTP 方法、路径、入参/出参 Schema 与元数据。\n * 客户端通过 `api.call(endpoint, input)` 调用,服务端通过\n * `kit.fromContract(endpoint, handler)` 响应,两端共享同一份契约。\n *\n * @typeParam TInput - 入参类型(由 Zod Schema 推导)\n * @typeParam TOutput - 出参类型(由 Zod Schema 推导)\n *\n * @example\n * ```ts\n * const loginEndpoint: EndpointDef<LoginInput, LoginOutput> = {\n * method: 'POST',\n * path: '/auth/login',\n * input: LoginInputSchema,\n * output: LoginOutputSchema,\n * requireAuth: false,\n * }\n * ```\n */\nexport interface EndpointDef<\n TInput = unknown,\n TOutput = unknown,\n> {\n /** HTTP 方法 */\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'\n /** 相对路径(相对于 API 前缀,如 '/auth/login') */\n path: string\n /** 入参 Zod Schema(GET 请求为 query params,其他为 body) */\n input: z.ZodType<TInput>\n /** 出参 Zod Schema */\n output: z.ZodType<TOutput>\n /** 是否需要认证(默认 true) */\n requireAuth?: boolean\n /** OpenAPI 描述元数据 */\n meta?: {\n summary?: string\n tags?: string[]\n }\n}\n\n// ─── Token ───\n\n/**\n * Token 对(与 @h-ai/iam 的 TokenPair 对齐)\n */\nexport interface TokenPair {\n /** Access Token */\n accessToken: string\n /** Refresh Token */\n refreshToken: string\n /** 过期时间(秒) */\n expiresIn: number\n /** Token 类型 */\n tokenType: 'Bearer'\n}\n\n/**\n * Token 存储适配器\n *\n * 可插拔存储后端(localStorage / memory / Capacitor Preferences / 自定义)。\n */\nexport interface TokenStorage {\n /** 获取 Access Token */\n getAccessToken: () => Promise<string | null>\n /** 设置 Access Token */\n setAccessToken: (token: string) => Promise<void>\n /** 获取 Refresh Token */\n getRefreshToken: () => Promise<string | null>\n /** 设置 Refresh Token */\n setRefreshToken: (token: string) => Promise<void>\n /** 清空所有 Token */\n clear: () => Promise<void>\n}\n\n// ─── 拦截器 ───\n\n/**\n * 请求配置(传递给拦截器)\n */\nexport interface RequestConfig {\n /** 完整 URL */\n url: string\n /** HTTP 方法 */\n method: string\n /** 请求头 */\n headers: Record<string, string>\n /** 请求体(已序列化) */\n body?: string | FormData\n /** 信号(abort) */\n signal?: AbortSignal\n}\n\n/** 请求拦截器 */\nexport type RequestInterceptor = (config: RequestConfig) => RequestConfig | Promise<RequestConfig>\n\n/** 响应拦截器 */\nexport type ResponseInterceptor = (response: Response) => Response | Promise<Response>\n\n// ─── 配置 ───\n\n/**\n * Token 刷新配置\n */\nexport interface AuthConfig {\n /** Token 存储适配器(默认 createLocalStorageTokenStorage) */\n storage?: TokenStorage\n /** Refresh Token 接口路径(相对于 baseUrl) */\n refreshUrl: string\n /** Token 刷新回调 */\n onTokenRefreshed?: (tokens: TokenPair) => void\n /** Token 刷新失败回调(通常用于跳转登录页) */\n onRefreshFailed?: () => void\n}\n\n/**\n * ApiClient 配置\n */\nexport interface ApiClientConfig {\n /** API 基础 URL(如 https://api.example.com/api/v1) */\n baseUrl: string\n /** Token 认证配置(省略则不启用自动 Token 管理) */\n auth?: AuthConfig\n /** 请求超时(毫秒,默认 30000) */\n timeout?: number\n /** 拦截器 */\n interceptors?: {\n request?: RequestInterceptor[]\n response?: ResponseInterceptor[]\n }\n /** 自定义 fetch 实现(用于测试或特殊环境) */\n fetch?: typeof globalThis.fetch\n}\n\n// ─── 上传选项 ───\n\n/**\n * 文件上传选项\n */\nexport interface UploadOptions {\n /** 文件字段名(默认 'file') */\n fieldName?: string\n /** 附加表单字段 */\n extraFields?: Record<string, string>\n}\n\n/**\n * 流式请求选项\n */\nexport interface StreamOptions {\n /** 外部取消信号(例如 AbortController.signal) */\n signal?: AbortSignal\n}\n\n// ─── ApiClient 接口 ───\n\n/**\n * Api Client 实例接口\n *\n * 提供通用 HTTP 方法和契约调用能力。\n */\nexport interface ApiClient {\n /** GET 请求 */\n get: <T>(path: string, params?: Record<string, unknown>) => Promise<HaiResult<T>>\n /** POST 请求 */\n post: <T>(path: string, body?: unknown) => Promise<HaiResult<T>>\n /** PUT 请求 */\n put: <T>(path: string, body?: unknown) => Promise<HaiResult<T>>\n /** PATCH 请求 */\n patch: <T>(path: string, body?: unknown) => Promise<HaiResult<T>>\n /** DELETE 请求 */\n delete: <T>(path: string, params?: Record<string, unknown>) => Promise<HaiResult<T>>\n\n /** 文件上传 */\n upload: (path: string, file: File | Blob, options?: UploadOptions) => Promise<HaiResult<unknown>>\n\n /** 流式请求(返回 AsyncIterable) */\n stream: (path: string, body?: unknown, options?: StreamOptions) => AsyncIterable<string>\n\n /**\n * 契约调用(推荐)\n *\n * 基于 EndpointDef 发起请求,路径、方法、入参、出参类型全由契约保证。\n */\n call: <TInput, TOutput>(\n endpoint: EndpointDef<TInput, TOutput>,\n input: TInput,\n ) => Promise<HaiResult<TOutput>>\n\n /** Token 管理 */\n auth: {\n /** 设置 Token */\n setTokens: (tokens: TokenPair) => Promise<void>\n /** 清空 Token */\n clear: () => Promise<void>\n /** Token 刷新回调,返回取消订阅函数 */\n onTokenRefreshed: (callback: (tokens: TokenPair) => void) => () => void\n }\n}\n\n/**\n * 辅助函数:创建端点定义(获得类型推导)\n *\n * @example\n * ```ts\n * const login = defineEndpoint({\n * method: 'POST',\n * path: '/auth/login',\n * input: LoginInputSchema,\n * output: LoginOutputSchema,\n * requireAuth: false,\n * })\n * ```\n */\nexport function defineEndpoint<TInput, TOutput>(\n def: EndpointDef<TInput, TOutput>,\n): EndpointDef<TInput, TOutput> {\n return def\n}\n\n// ─── ApiClientFunctions ───\n\n/**\n * API 客户端函数接口(单例模式)\n *\n * 统一的 API 客户端访问入口:\n * - `api.init(config)` — 初始化客户端\n * - `api.close()` — 关闭客户端并释放资源\n * - `api.get / post / put / patch / delete` — 通用 HTTP 方法\n * - `api.call(endpoint, input)` — 契约调用\n * - `api.upload(path, file)` — 文件上传\n * - `api.stream(path, body)` — 流式请求\n * - `api.auth` — Token 管理\n * - `api.config` — 当前配置(未初始化时为 null)\n * - `api.isInitialized` — 初始化状态\n */\nexport interface ApiClientFunctions {\n /**\n * 初始化 API 客户端\n *\n * 已有实例时会先 close 再重新初始化。\n *\n * @param config - 客户端配置\n * @returns 成功 ok(undefined);失败返回 err(含 ApiClientError)\n */\n init: (config: ApiClientConfig) => Promise<HaiResult<void>>\n\n /**\n * 关闭 API 客户端并释放资源\n *\n * 重复调用不会报错。\n */\n close: () => Promise<void>\n\n /** 当前客户端配置;未初始化或已关闭时为 null */\n readonly config: ApiClientConfig | null\n\n /** 是否已完成初始化 */\n readonly isInitialized: boolean\n\n /** GET 请求 */\n readonly get: ApiClient['get']\n /** POST 请求 */\n readonly post: ApiClient['post']\n /** PUT 请求 */\n readonly put: ApiClient['put']\n /** PATCH 请求 */\n readonly patch: ApiClient['patch']\n /** DELETE 请求 */\n readonly delete: ApiClient['delete']\n /** 文件上传 */\n readonly upload: ApiClient['upload']\n\n /**\n * 流式请求(返回 AsyncIterable)\n *\n * @throws 未初始化时抛出异常(async generator 无法返回 HaiResult)\n */\n readonly stream: ApiClient['stream']\n\n /** 契约调用(推荐) */\n readonly call: ApiClient['call']\n\n /** Token 管理 */\n readonly auth: ApiClient['auth']\n}\n","/**\n * @h-ai/api-client — 契约调用\n *\n * 基于 EndpointDef 发起类型安全的 API 请求。\n * @module api-client-contract\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { FetchClient } from './api-client-fetch.js'\nimport type { EndpointDef } from './api-client-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { apiClientM } from './api-client-i18n.js'\nimport { HaiApiClientError } from './api-client-types.js'\n\n/**\n * 创建契约调用函数\n *\n * @param fetchClient - Fetch Client 实例\n * @returns call 函数\n */\nexport function createContractCaller(fetchClient: FetchClient) {\n /**\n * 基于端点契约发起请求\n *\n * 路径、方法、入参、出参类型全由契约保证。\n *\n * @typeParam TInput - 入参类型\n * @typeParam TOutput - 出参类型\n * @param endpoint - 端点契约定义\n * @param input - 入参数据\n * @returns 出参 HaiResult\n *\n * @example\n * ```ts\n * const result = await api.call(iamEndpoints.login, { identifier: 'alice', password: 'xxx' })\n * ```\n */\n async function call<TInput, TOutput>(\n endpoint: EndpointDef<TInput, TOutput>,\n input: TInput,\n ): Promise<HaiResult<TOutput>> {\n // 客户端入参校验(避免无效请求发送到服务端)\n const parsed = endpoint.input.safeParse(input)\n if (!parsed.success) {\n return err(\n HaiApiClientError.VALIDATION_FAILED,\n apiClientM('apiClient_validationFailed'),\n parsed.error.issues,\n )\n }\n\n const validInput = parsed.data\n\n let result: HaiResult<unknown>\n\n switch (endpoint.method) {\n case 'GET':\n result = await fetchClient.get(endpoint.path, validInput as Record<string, unknown>)\n break\n\n case 'POST':\n result = await fetchClient.post(endpoint.path, validInput)\n break\n\n case 'PUT':\n result = await fetchClient.put(endpoint.path, validInput)\n break\n\n case 'PATCH':\n result = await fetchClient.patch(endpoint.path, validInput)\n break\n\n case 'DELETE':\n result = await fetchClient.delete(endpoint.path, validInput as Record<string, unknown>)\n break\n\n default:\n return err(\n HaiApiClientError.UNKNOWN,\n apiClientM('apiClient_unknown'),\n )\n }\n\n if (!result.success) {\n return result\n }\n\n // 响应数据校验(确保服务端返回的数据符合契约)\n const outputParsed = endpoint.output.safeParse(result.data)\n if (!outputParsed.success) {\n return err(\n HaiApiClientError.VALIDATION_FAILED,\n apiClientM('apiClient_responseValidationFailed'),\n outputParsed.error.issues,\n )\n }\n\n return ok(outputParsed.data)\n }\n\n return call\n}\n","/**\n * @h-ai/api-client — 错误处理\n *\n * HTTP 状态码到 HaiResult 错误的统一转换。\n * @module api-client-error\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport { err } from '@h-ai/core'\nimport { apiClientM } from './api-client-i18n.js'\nimport { HaiApiClientError } from './api-client-types.js'\n\n/**\n * 将 HTTP 响应转为 HaiResult<never>\n *\n * @param response - HTTP 响应\n * @returns HaiResult<never>\n */\nexport async function responseToError(response: Response): Promise<HaiResult<never>> {\n let details: unknown\n\n try {\n const body = await response.json()\n details = body\n }\n catch {\n // 响应体不是 JSON,忽略\n }\n\n const status = response.status\n\n if (status === 401) {\n return err(\n HaiApiClientError.UNAUTHORIZED,\n apiClientM('apiClient_unauthorized'),\n details,\n )\n }\n\n if (status === 403) {\n return err(\n HaiApiClientError.FORBIDDEN,\n apiClientM('apiClient_forbidden'),\n details,\n )\n }\n\n if (status === 404) {\n return err(\n HaiApiClientError.NOT_FOUND,\n apiClientM('apiClient_notFound'),\n details,\n )\n }\n\n if (status === 400 || status === 422) {\n return err(\n HaiApiClientError.VALIDATION_FAILED,\n apiClientM('apiClient_validationFailed'),\n details,\n )\n }\n\n if (status >= 500) {\n return err(\n HaiApiClientError.SERVER_ERROR,\n apiClientM('apiClient_serverError', { params: { status: String(status) } }),\n details,\n )\n }\n\n return err(\n HaiApiClientError.UNKNOWN,\n apiClientM('apiClient_unknown'),\n details,\n )\n}\n\n/**\n * 将网络异常转为 HaiResult<never>\n *\n * @param cause - 原始异常\n * @returns HaiResult<never>\n */\nexport function networkErrorToResult(cause: unknown): HaiResult<never> {\n // AbortError = 超时\n if (cause instanceof DOMException && cause.name === 'AbortError') {\n return err(\n HaiApiClientError.TIMEOUT,\n apiClientM('apiClient_timeout'),\n cause,\n )\n }\n\n return err(\n HaiApiClientError.NETWORK_ERROR,\n apiClientM('apiClient_networkError'),\n cause,\n )\n}\n","/**\n * @h-ai/api-client — 核心 fetch 封装\n *\n * 请求/响应拦截器链、超时控制、Token 自动附加、401 自动刷新重试。\n * @module api-client-fetch\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { TokenManager } from './api-client-token-manager.js'\nimport type {\n ApiClientConfig,\n RequestConfig,\n RequestInterceptor,\n ResponseInterceptor,\n StreamOptions,\n UploadOptions,\n} from './api-client-types.js'\nimport { core, ok } from '@h-ai/core'\nimport { networkErrorToResult, responseToError } from './api-client-error.js'\nimport { apiClientM } from './api-client-i18n.js'\n\nconst logger = core.logger.child({ module: 'api-client', scope: 'fetch' })\n\n/** 默认超时 30s */\nconst DEFAULT_TIMEOUT = 30_000\n\n/**\n * 创建核心请求函数\n *\n * 封装 fetch 请求,集成:\n * - 请求/响应拦截器链\n * - 超时控制(AbortController)\n * - Token 自动附加(Authorization: Bearer)\n * - 401 自动刷新 + 重试(一次)\n *\n * @param config - ApiClient 配置\n * @param tokenManager - Token 管理器(可选)\n * @returns 请求函数集合\n */\nexport function createFetchClient(\n config: ApiClientConfig,\n tokenManager?: TokenManager,\n) {\n const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis)\n const timeout = config.timeout ?? DEFAULT_TIMEOUT\n const requestInterceptors: RequestInterceptor[] = config.interceptors?.request ?? []\n const responseInterceptors: ResponseInterceptor[] = config.interceptors?.response ?? []\n\n /**\n * 构建完整 URL\n */\n function buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = config.baseUrl.replace(/\\/+$/, '')\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n const url = new URL(`${base}${normalizedPath}`)\n\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value))\n }\n }\n }\n\n return url.toString()\n }\n\n /**\n * 执行请求(含拦截器、超时、Token、401 重试)\n */\n async function execute<T>(\n requestConfig: RequestConfig,\n retryOnUnauthorized = true,\n ): Promise<HaiResult<T>> {\n logger.trace('Executing request', { method: requestConfig.method, url: requestConfig.url })\n\n // 1. Token 注入\n if (tokenManager) {\n const accessToken = await tokenManager.storage.getAccessToken()\n if (accessToken) {\n requestConfig.headers.Authorization = `Bearer ${accessToken}`\n }\n }\n\n // 2. 请求拦截器\n let finalConfig = requestConfig\n for (const interceptor of requestInterceptors) {\n finalConfig = await interceptor(finalConfig)\n }\n\n // 3. 超时控制\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n finalConfig.signal = controller.signal\n\n try {\n let response = await fetchFn(finalConfig.url, {\n method: finalConfig.method,\n headers: finalConfig.headers,\n body: finalConfig.body,\n signal: finalConfig.signal,\n })\n\n clearTimeout(timeoutId)\n\n // 4. 响应拦截器\n for (const interceptor of responseInterceptors) {\n response = await interceptor(response)\n }\n\n // 5. 401 自动刷新 + 重试\n if (response.status === 401 && retryOnUnauthorized && tokenManager) {\n logger.trace('Received 401, attempting token refresh', { url: requestConfig.url })\n const tokens = await tokenManager.refresh()\n if (tokens) {\n logger.trace('Token refreshed, retrying request', { url: requestConfig.url })\n requestConfig.headers.Authorization = `Bearer ${tokens.accessToken}`\n return execute<T>(requestConfig, false)\n }\n logger.warn('Token refresh failed, returning 401 error', { url: requestConfig.url })\n }\n\n // 6. 错误处理\n if (!response.ok) {\n return responseToError(response)\n }\n\n // 7. 解析成功响应\n const contentType = response.headers.get('content-type') ?? ''\n if (contentType.includes('application/json')) {\n const body = await response.json() as { data?: T, success?: boolean }\n // 兼容标准 HaiResult 响应格式 { success, data }\n if (body && typeof body === 'object' && 'data' in body) {\n return ok(body.data as T)\n }\n return ok(body as T)\n }\n\n // 非 JSON 响应(204 No Content 等)\n return ok(undefined as T)\n }\n catch (cause) {\n clearTimeout(timeoutId)\n logger.warn('Request failed', { url: requestConfig.url, error: cause })\n return networkErrorToResult(cause)\n }\n }\n\n // ─── 通用 HTTP 方法 ───\n\n async function get<T>(path: string, params?: Record<string, unknown>): Promise<HaiResult<T>> {\n return execute<T>({\n url: buildUrl(path, params),\n method: 'GET',\n headers: { Accept: 'application/json' },\n })\n }\n\n async function post<T>(path: string, body?: unknown): Promise<HaiResult<T>> {\n return execute<T>({\n url: buildUrl(path),\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n }\n\n async function put<T>(path: string, body?: unknown): Promise<HaiResult<T>> {\n return execute<T>({\n url: buildUrl(path),\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n }\n\n async function patch<T>(path: string, body?: unknown): Promise<HaiResult<T>> {\n return execute<T>({\n url: buildUrl(path),\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n }\n\n async function del<T>(path: string, params?: Record<string, unknown>): Promise<HaiResult<T>> {\n return execute<T>({\n url: buildUrl(path, params),\n method: 'DELETE',\n headers: { Accept: 'application/json' },\n })\n }\n\n // ─── 文件上传 ───\n\n async function upload(\n path: string,\n file: File | Blob,\n options?: UploadOptions,\n ): Promise<HaiResult<unknown>> {\n const formData = new FormData()\n formData.append(options?.fieldName ?? 'file', file)\n\n if (options?.extraFields) {\n for (const [key, value] of Object.entries(options.extraFields)) {\n formData.append(key, value)\n }\n }\n\n return execute({\n url: buildUrl(path),\n method: 'POST',\n headers: { Accept: 'application/json' },\n body: formData,\n })\n }\n\n // ─── 流式请求 ───\n\n async function* stream(path: string, body?: unknown, options?: StreamOptions): AsyncIterable<string> {\n const requestConfig: RequestConfig = {\n url: buildUrl(path),\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n }\n\n // Token 注入\n if (tokenManager) {\n const accessToken = await tokenManager.storage.getAccessToken()\n if (accessToken) {\n requestConfig.headers.Authorization = `Bearer ${accessToken}`\n }\n }\n\n // 请求拦截器\n let finalConfig = requestConfig\n for (const interceptor of requestInterceptors) {\n finalConfig = await interceptor(finalConfig)\n }\n\n // 超时控制 + 外部 abort\n const controller = new AbortController()\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n let timeoutTriggered = false\n\n const clearStreamTimeout = () => {\n if (timeoutId) {\n clearTimeout(timeoutId)\n timeoutId = null\n }\n }\n\n const armStreamTimeout = () => {\n clearStreamTimeout()\n timeoutTriggered = false\n timeoutId = setTimeout(() => {\n timeoutTriggered = true\n controller.abort()\n }, timeout)\n }\n\n const externalSignal = options?.signal\n const onExternalAbort = () => {\n controller.abort()\n }\n\n if (externalSignal) {\n if (externalSignal.aborted) {\n controller.abort()\n }\n else {\n externalSignal.addEventListener('abort', onExternalAbort, { once: true })\n }\n }\n\n async function doStreamFetch(): Promise<Response> {\n armStreamTimeout()\n try {\n return await fetchFn(finalConfig.url, {\n method: finalConfig.method,\n headers: finalConfig.headers,\n body: finalConfig.body,\n signal: controller.signal,\n })\n }\n catch (cause) {\n clearStreamTimeout()\n throw cause\n }\n }\n\n // 统一资源清理:无论 doStreamFetch / 401 重试 / 读取阶段在哪抛出异常,\n // 都确保清理超时定时器、外部信号监听器和 reader 锁。\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null\n\n try {\n let response = await doStreamFetch()\n\n // 401 自动刷新 + 重试(一次)\n if (response.status === 401 && tokenManager) {\n logger.info('Stream received 401, attempting token refresh', { url: requestConfig.url })\n const tokens = await tokenManager.refresh()\n if (tokens) {\n finalConfig.headers.Authorization = `Bearer ${tokens.accessToken}`\n response = await doStreamFetch()\n }\n }\n\n if (!response.ok) {\n const errorResult = await responseToError(response)\n if (!errorResult.success) {\n throw new Error(errorResult.error.message, { cause: errorResult.error })\n }\n }\n\n if (!response.body) {\n throw new Error(apiClientM('apiClient_networkError'))\n }\n\n reader = response.body.getReader()\n const decoder = new TextDecoder()\n let lineBuffer = ''\n\n while (true) {\n armStreamTimeout()\n const { done, value } = await reader.read()\n clearStreamTimeout()\n if (done)\n break\n\n lineBuffer += decoder.decode(value, { stream: true })\n // 按换行分割,保留最后一段(可能是不完整行)\n const parts = lineBuffer.split('\\n')\n lineBuffer = parts.pop() ?? ''\n\n for (const line of parts) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6)\n if (data !== '[DONE]') {\n yield data\n }\n }\n }\n }\n\n // 处理缓冲区剩余内容\n if (lineBuffer.startsWith('data: ')) {\n const data = lineBuffer.slice(6)\n if (data !== '[DONE]') {\n yield data\n }\n }\n }\n catch (cause) {\n if (cause instanceof DOMException && cause.name === 'AbortError') {\n if (externalSignal?.aborted && !timeoutTriggered) {\n throw cause\n }\n throw new Error(apiClientM('apiClient_timeout'), { cause })\n }\n throw cause\n }\n finally {\n clearStreamTimeout()\n if (externalSignal) {\n externalSignal.removeEventListener('abort', onExternalAbort)\n }\n if (reader) {\n reader.releaseLock()\n }\n }\n }\n\n return { get, post, put, patch, delete: del, upload, stream, execute, buildUrl }\n}\n\n/** Fetch Client 类型 */\nexport type FetchClient = ReturnType<typeof createFetchClient>\n","/**\n * @h-ai/api-client — Token 管理器(内部)\n *\n * 管理 Token 存储和自动刷新逻辑,仅供模块内部使用,不对外导出。\n * @module api-client-token-manager\n * @internal\n */\n\nimport type { TokenPair, TokenStorage } from './api-client-types.js'\nimport { core } from '@h-ai/core'\nimport { z } from 'zod'\n\nconst logger = core.logger.child({ module: 'api-client', scope: 'auth' })\n\n/**\n * Token 刷新响应校验 Schema\n */\nconst TokenPairSchema = z.object({\n accessToken: z.string().min(1),\n refreshToken: z.string().min(1),\n expiresIn: z.number().optional(),\n tokenType: z.string().optional(),\n})\n\n/**\n * Token 刷新回调\n */\ntype RefreshCallback = (tokens: TokenPair) => void\n\n/**\n * 创建 Token 管理器\n *\n * 内部使用,管理 Token 存储和刷新逻辑。\n *\n * @param storage - Token 存储适配器\n * @param refreshUrl - Refresh Token 接口完整 URL\n * @param fetchFn - fetch 实现\n * @param onRefreshFailed - 刷新失败回调\n * @returns Token 管理器\n */\nexport function createTokenManager(\n storage: TokenStorage,\n refreshUrl: string,\n fetchFn: typeof globalThis.fetch,\n onRefreshFailed?: () => void,\n) {\n const callbacks: RefreshCallback[] = []\n let refreshPromise: Promise<TokenPair | null> | null = null\n\n /**\n * 刷新 Token(带并发去重)\n *\n * 多个并发 401 只发一次 refresh 请求。\n */\n async function refresh(): Promise<TokenPair | null> {\n // 去重:已有刷新请求进行中,复用\n if (refreshPromise) {\n return refreshPromise\n }\n\n refreshPromise = doRefresh()\n try {\n return await refreshPromise\n }\n finally {\n refreshPromise = null\n }\n }\n\n async function doRefresh(): Promise<TokenPair | null> {\n const refreshToken = await storage.getRefreshToken()\n if (!refreshToken) {\n logger.warn('Token refresh skipped, no refresh token available')\n onRefreshFailed?.()\n return null\n }\n\n logger.debug('Refreshing token', { url: refreshUrl })\n\n try {\n const response = await fetchFn(refreshUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken }),\n })\n\n if (!response.ok) {\n await storage.clear()\n onRefreshFailed?.()\n return null\n }\n\n const body = await response.json() as { data?: unknown }\n const parsed = TokenPairSchema.safeParse(body.data)\n if (!parsed.success) {\n logger.warn('Token refresh returned invalid data', { issues: parsed.error.issues })\n await storage.clear()\n onRefreshFailed?.()\n return null\n }\n\n // 提供默认值,避免 as TokenPair 不安全强转\n const tokens: TokenPair = {\n accessToken: parsed.data.accessToken,\n refreshToken: parsed.data.refreshToken,\n expiresIn: parsed.data.expiresIn ?? 3600,\n tokenType: 'Bearer',\n }\n await storage.setAccessToken(tokens.accessToken)\n await storage.setRefreshToken(tokens.refreshToken)\n logger.info('Token refreshed successfully')\n\n // 通知回调\n for (const cb of callbacks) {\n cb(tokens)\n }\n\n return tokens\n }\n catch (error) {\n logger.error('Token refresh failed', { error })\n await storage.clear()\n onRefreshFailed?.()\n return null\n }\n }\n\n return {\n storage,\n refresh,\n\n async setTokens(tokens: TokenPair) {\n await storage.setAccessToken(tokens.accessToken)\n await storage.setRefreshToken(tokens.refreshToken)\n },\n\n async clear() {\n await storage.clear()\n },\n\n onTokenRefreshed(callback: RefreshCallback): () => void {\n callbacks.push(callback)\n return () => {\n const idx = callbacks.indexOf(callback)\n if (idx >= 0)\n callbacks.splice(idx, 1)\n }\n },\n }\n}\n\n/** Token 管理器类型 */\nexport type TokenManager = ReturnType<typeof createTokenManager>\n","/**\n * @h-ai/api-client — 模块入口\n *\n * 提供统一的 `api` 对象,管理 HTTP 客户端运行时状态与生命周期。\n * @module api-client-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ApiClient, ApiClientConfig, ApiClientFunctions, TokenPair } from './api-client-types.js'\n\nimport { core, err, ok } from '@h-ai/core'\nimport { createLocalStorageTokenStorage } from './api-client-auth.js'\nimport { createContractCaller } from './api-client-contract.js'\nimport { createFetchClient } from './api-client-fetch.js'\nimport { apiClientM } from './api-client-i18n.js'\nimport { createTokenManager } from './api-client-token-manager.js'\nimport { HaiApiClientError } from './api-client-types.js'\n\nconst logger = core.logger.child({ module: 'api-client', scope: 'main' })\n\n// ─── 内部状态 ───\n\n/** 当前 ApiClient 实例(init 后非空,close 后置空) */\nlet currentClient: ApiClient | null = null\n\n/** 当前客户端配置(init 后非空,close 后置空) */\nlet currentConfig: ApiClientConfig | null = null\n\n/** 并发初始化防护标志 */\nlet initInProgress = false\n\n// ─── 内部工厂 ───\n\n/**\n * 根据配置创建 ApiClient 实例\n *\n * 仅供 init() 内部使用,不对外暴露。\n */\nfunction createClient(config: ApiClientConfig): ApiClient {\n // 优先使用外部传入的 fetch(便于测试/跨平台注入);未传入时回退到全局 fetch\n const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis)\n // auth 开启时,默认使用 localStorage 作为 Token 存储\n const tokenStorage = config.auth?.storage ?? createLocalStorageTokenStorage()\n\n // Token 管理器(可选)\n const tokenManager = config.auth\n ? createTokenManager(\n tokenStorage,\n `${config.baseUrl.replace(/\\/+$/, '')}${config.auth.refreshUrl}`,\n fetchFn,\n config.auth.onRefreshFailed,\n )\n : undefined\n\n // 注册外部回调\n if (config.auth?.onTokenRefreshed && tokenManager) {\n tokenManager.onTokenRefreshed(config.auth.onTokenRefreshed)\n }\n\n // Fetch Client\n const fetchClient = createFetchClient(config, tokenManager)\n\n // 契约调用\n const call = createContractCaller(fetchClient)\n\n // Auth 管理接口\n const auth: ApiClient['auth'] = {\n async setTokens(tokens: TokenPair) {\n if (tokenManager) {\n await tokenManager.setTokens(tokens)\n }\n },\n async clear() {\n if (tokenManager) {\n await tokenManager.clear()\n }\n },\n onTokenRefreshed(callback: (tokens: TokenPair) => void): () => void {\n if (tokenManager) {\n return tokenManager.onTokenRefreshed(callback)\n }\n return () => { }\n },\n }\n\n return {\n get: fetchClient.get,\n post: fetchClient.post,\n put: fetchClient.put,\n patch: fetchClient.patch,\n delete: fetchClient.delete,\n upload: fetchClient.upload,\n stream: fetchClient.stream,\n call,\n auth,\n }\n}\n\n// ─── 未初始化占位 ───\n\n/**\n * 未初始化状态的错误工具包\n *\n * 当 api 未调用 init() 就直接使用 HTTP 方法时,\n * 所有方法调用都会返回 NOT_INITIALIZED 错误。\n */\nconst notInitialized = core.module.createNotInitializedKit(\n HaiApiClientError.NOT_INITIALIZED,\n () => apiClientM('apiClient_notInitialized'),\n)\n\n/** 未初始化时的 HTTP 操作占位代理(get/post/put/patch/delete/upload/call) */\nconst notInitializedOps = notInitialized.proxy<Pick<ApiClient, 'get' | 'post' | 'put' | 'patch' | 'delete' | 'upload' | 'call'>>()\n\n/** 未初始化时的 Token 管理占位 */\nconst notInitializedAuth: ApiClient['auth'] = {\n async setTokens() {\n logger.warn('api.auth.setTokens called before initialization, ignored')\n },\n async clear() {\n logger.warn('api.auth.clear called before initialization, ignored')\n },\n onTokenRefreshed() {\n logger.warn('api.auth.onTokenRefreshed called before initialization, ignored')\n return () => { }\n },\n}\n\n/**\n * 未初始化时的流式请求占位\n *\n * async generator 无法返回 HaiResult,迭代时抛出异常。\n */\nfunction notInitializedStreamFn(_path: string, _body?: unknown, _options?: { signal?: AbortSignal }): AsyncIterable<string> {\n return {\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<string>> {\n throw new Error(apiClientM('apiClient_notInitialized'))\n },\n }\n },\n }\n}\n\n// ─── API 客户端单例 ───\n\n/**\n * API 客户端单例\n *\n * 使用前必须先调用 `api.init()` 初始化,传入 baseUrl 等配置信息。\n * 初始化后通过 `api.get`、`api.post`、`api.call` 等方法发起请求。\n *\n * @example\n * ```ts\n * import { api } from '@h-ai/api-client'\n *\n * // 初始化\n * await api.init({\n * baseUrl: 'https://api.example.com/api/v1',\n * auth: { refreshUrl: '/auth/refresh' },\n * })\n *\n * // 契约调用\n * const result = await api.call(loginEndpoint, { identifier: 'alice', password: 'xxx' })\n *\n * // 通用 HTTP\n * const users = await api.get<User[]>('/users', { page: 1 })\n *\n * // 关闭\n * await api.close()\n * ```\n */\nexport const api: ApiClientFunctions = {\n /**\n * 初始化 API 客户端\n *\n * 如果当前已有活跃实例,会先自动 close 再重新初始化。\n * 创建异常时返回 CONFIG_ERROR 错误。\n *\n * @param config 客户端配置(baseUrl、auth 等)\n * @returns 成功时返回 ok(undefined);失败时返回包含错误码和消息的 err。\n */\n async init(config: ApiClientConfig): Promise<HaiResult<void>> {\n if (initInProgress) {\n return err(\n HaiApiClientError.CONFIG_ERROR,\n apiClientM('apiClient_configError', { params: { error: 'initialization already in progress' } }),\n )\n }\n\n initInProgress = true\n try {\n if (currentClient) {\n logger.warn('Api-client module is already initialized, reinitializing')\n await api.close()\n }\n\n logger.info('Initializing api-client module')\n\n currentClient = createClient(config)\n currentConfig = config\n logger.info('Api-client module initialized')\n return ok(undefined)\n }\n catch (error) {\n logger.error('Api-client module initialization failed', { error })\n return err(\n HaiApiClientError.CONFIG_ERROR,\n apiClientM('apiClient_configError', { params: { error: error instanceof Error ? error.message : String(error) } }),\n error,\n )\n }\n finally {\n initInProgress = false\n }\n },\n\n /** GET 请求。未初始化时返回 NOT_INITIALIZED 错误 */\n get get() { return currentClient?.get ?? notInitializedOps.get },\n /** POST 请求。未初始化时返回 NOT_INITIALIZED 错误 */\n get post() { return currentClient?.post ?? notInitializedOps.post },\n /** PUT 请求。未初始化时返回 NOT_INITIALIZED 错误 */\n get put() { return currentClient?.put ?? notInitializedOps.put },\n /** PATCH 请求。未初始化时返回 NOT_INITIALIZED 错误 */\n get patch() { return currentClient?.patch ?? notInitializedOps.patch },\n /** DELETE 请求。未初始化时返回 NOT_INITIALIZED 错误 */\n get delete() { return currentClient?.delete ?? notInitializedOps.delete },\n /** 文件上传。未初始化时返回 NOT_INITIALIZED 错误 */\n get upload() { return currentClient?.upload ?? notInitializedOps.upload },\n /** 契约调用。未初始化时返回 NOT_INITIALIZED 错误 */\n get call() { return currentClient?.call ?? notInitializedOps.call },\n\n /**\n * 流式请求。未初始化时迭代抛出异常。\n *\n * @throws 未初始化时抛出 Error\n */\n get stream() { return currentClient?.stream ?? notInitializedStreamFn },\n\n /** Token 管理接口。未初始化时为空操作 */\n get auth() { return currentClient?.auth ?? notInitializedAuth },\n\n /** 当前客户端配置;未初始化或已关闭时为 null */\n get config() { return currentConfig },\n\n /** 是否已完成初始化 */\n get isInitialized() { return currentClient !== null },\n\n /**\n * 关闭 API 客户端并释放资源\n *\n * 关闭后所有 HTTP 操作将返回 NOT_INITIALIZED 错误。\n * 重复调用不会报错。\n */\n async close() {\n if (!currentClient) {\n currentConfig = null\n return\n }\n\n logger.info('Closing api-client module')\n\n currentClient = null\n currentConfig = null\n\n logger.info('Api-client module closed')\n },\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/api-client-auth.ts","../messages/en-US.json","../messages/ja-JP.json","../messages/zh-CN.json","../src/api-client-i18n.ts","../src/api-client-iam.ts","../src/api-client-types.ts","../src/api-client-main.ts"],"names":["core","accessToken"],"mappings":";;;;;;;;AAkCA,IAAM,MAAA,GAAS,KAAK,MAAA,CAAO,KAAA,CAAM,EAAE,MAAA,EAAQ,YAAA,EAAc,KAAA,EAAO,MAAA,EAAQ,CAAA;AAQxE,IAAM,wBAAA,GAA2B,qBAAA;AAoB1B,SAAS,wBAAA,GAAyC;AACvD,EAAA,IAAI,WAAA,GAA6B,IAAA;AACjC,EAAA,IAAI,YAAA,GAA8B,IAAA;AAClC,EAAA,OAAO;AAAA,IACL,MAAM,cAAA,GAAiB;AAAE,MAAA,OAAO,WAAA;AAAA,IAAY,CAAA;AAAA,IAC5C,MAAM,eAAe,KAAA,EAAO;AAAE,MAAA,WAAA,GAAc,KAAA;AAAA,IAAM,CAAA;AAAA,IAClD,MAAM,eAAA,GAAkB;AAAE,MAAA,OAAO,YAAA;AAAA,IAAa,CAAA;AAAA,IAC9C,MAAM,gBAAgB,KAAA,EAAO;AAAE,MAAA,YAAA,GAAe,KAAA;AAAA,IAAM,CAAA;AAAA,IACpD,MAAM,KAAA,GAAQ;AACZ,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,YAAA,GAAe,IAAA;AAAA,IACjB;AAAA,GACF;AACF;AAwBO,SAAS,gCAAA,GAAiD;AAE/D,EAAA,IAAI,iBAAA,GAAmC,IAAA;AACvC,EAAA,OAAO;AAAA,IACL,MAAM,cAAA,GAAiB;AAAE,MAAA,OAAO,iBAAA;AAAA,IAAkB,CAAA;AAAA,IAClD,MAAM,eAAe,KAAA,EAAO;AAAE,MAAA,iBAAA,GAAoB,KAAA;AAAA,IAAM,CAAA;AAAA;AAAA;AAAA,IAGxD,MAAM,eAAA,GAAkB;AAAE,MAAA,OAAO,wBAAA;AAAA,IAAyB,CAAA;AAAA;AAAA,IAE1D,MAAM,gBAAgB,MAAA,EAAQ;AAAA,IAAsC,CAAA;AAAA,IACpE,MAAM,KAAA,GAAQ;AACZ,MAAA,iBAAA,GAAoB,IAAA;AAAA,IAGtB;AAAA,GACF;AACF;AAKA,IAAM,yBAAA,GAA4B,EAAE,MAAA,CAAO;AAAA,EACzC,WAAA,EAAa,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC7B,cAAc,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,EAAS;AAAA,EACzC,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC/B,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AACxB,CAAC,CAAA;AAuCM,SAAS,mBACd,OAAA,EACA,kBAAA,EACA,OAAA,EACA,eAAA,EACA,mBAA2B,GAAA,EAC3B;AACA,EAAA,MAAM,YAA+B,EAAC;AACtC,EAAA,IAAI,cAAA,GAAmD,IAAA;AACvD,EAAA,IAAI,iBAAA,GAA4C,IAAA;AAChD,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,cAAA,GAAiB,KAAA;AACrB,EAAA,IAAI,eAAA,GAAiC,QAAQ,OAAA,EAAQ;AAcrD,EAAA,SAAS,cAAiB,SAAA,EAAyC;AACjE,IAAA,MAAM,OAAA,GAAU,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA;AAC9C,IAAA,eAAA,GAAkB,OAAA,CAAQ,KAAK,MAAM;AAAA,IAAC,GAAG,MAAM;AAAA,IAAC,CAAC,CAAA;AACjD,IAAA,OAAO,OAAA;AAAA,EACT;AAaA,EAAA,SAAS,iBAAA,GAA4C;AACnD,IAAA,MAAM,kBAAA,GAAqB,iBAAA;AAC3B,IAAA,UAAA,EAAA;AACA,IAAA,iBAAA,GAAoB,IAAA;AACpB,IAAA,cAAA,GAAiB,IAAA;AACjB,IAAA,OAAO,kBAAA;AAAA,EACT;AAaA,EAAA,eAAe,YAAY,iBAAA,EAA0C;AACnE,IAAA,MAAM,cAAc,YAAY;AAC9B,MAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,QAAA;AACF,MAAA,MAAM,QAAQ,KAAA,EAAM;AACpB,MAAA,IAAI,sBAAsB,UAAA,EAAY;AACpC,QAAA,cAAA,GAAiB,IAAA;AACjB,QAAA,eAAA,IAAkB;AAAA,MACpB;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAYA,EAAA,eAAe,OAAA,GAAqC;AAElD,IAAA,IAAI,cAAA;AACF,MAAA,OAAO,cAAA;AACT,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,UAAA,EAAY,UAAU,CAAA;AAChD,IAAA,iBAAA,GAAoB,UAAA;AACpB,IAAA,cAAA,GAAiB,OAAA;AACjB,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,OAAA;AAAA,IACf,CAAA,SACA;AAEE,MAAA,IAAI,mBAAmB,OAAA,EAAS;AAC9B,QAAA,cAAA,GAAiB,IAAA;AACjB,QAAA,iBAAA,GAAoB,IAAA;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAgBA,EAAA,eAAe,SAAA,CAAU,mBAA2B,UAAA,EAAwD;AAE1G,IAAA,MAAM,eAAA;AACN,IAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,MAAA,OAAO,IAAA;AACT,IAAA,MAAM,YAAA,GAAe,MAAM,OAAA,CAAQ,eAAA,EAAgB;AACnD,IAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,MAAA,OAAO,IAAA;AACT,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,cAAA,GAAiB,IAAA;AACjB,MAAA,MAAA,CAAO,MAAM,mDAAmD,CAAA;AAChE,MAAA,eAAA,IAAkB;AAClB,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,MAAA,CAAO,MAAM,kBAAkB,CAAA;AAE/B,IAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,gBAAgB,CAAA;AACvE,IAAA,IAAI;AAGF,MAAA,MAAM,oBAAoB,YAAA,KAAiB,wBAAA;AAC3C,MAAA,MAAM,WAAA,GAA2B;AAAA,QAC/B,MAAA,EAAQ,MAAA;AAAA,QACR,MAAM,iBAAA,GAAoB,KAAA,CAAA,GAAY,KAAK,SAAA,CAAU,EAAE,cAAc,CAAA;AAAA,QACrE,WAAA,EAAa,oBAAoB,SAAA,GAAY,aAAA;AAAA,QAC7C,QAAQ,UAAA,CAAW;AAAA,OACrB;AACA,MAAA,IAAI,CAAC,iBAAA,EAAmB;AACtB,QAAA,WAAA,CAAY,OAAA,GAAU,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC7D;AACA,MAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,kBAAA,EAAoB,WAAW,CAAA;AAC9D,MAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,QAAA,OAAO,IAAA;AAET,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAGhB,QAAA,IAAI,QAAA,CAAS,MAAA,IAAU,GAAA,IAAO,QAAA,CAAS,SAAS,GAAA,EAAK;AACnD,UAAA,MAAM,YAAY,iBAAiB,CAAA;AAAA,QACrC,CAAA,MACK;AACH,UAAA,MAAA,CAAO,KAAK,iDAAA,EAAmD,EAAE,MAAA,EAAQ,QAAA,CAAS,QAAQ,CAAA;AAAA,QAC5F;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,MAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,QAAA,OAAO,IAAA;AACT,MAAA,MAAM,SAAS,yBAAA,CAA0B,SAAA,CAAU,gBAAA,CAAiB,IAAA,CAAK,IAAI,CAAC,CAAA;AAC9E,MAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,QAAA,MAAA,CAAO,KAAK,qCAAA,EAAuC,EAAE,QAAQ,MAAA,CAAO,KAAA,CAAM,QAAQ,CAAA;AAClF,QAAA,MAAM,YAAY,iBAAiB,CAAA;AACnC,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,IAAI,CAAC,iBAAA,IAAqB,CAAC,MAAA,CAAO,KAAK,YAAA,EAAc;AACnD,QAAA,MAAA,CAAO,IAAA,CAAK,qCAAA,EAAuC,EAAE,MAAA,EAAQ,CAAC,EAAE,IAAA,EAAM,CAAC,cAAc,CAAA,EAAG,OAAA,EAAS,UAAA,EAAY,GAAG,CAAA;AAChH,QAAA,MAAM,YAAY,iBAAiB,CAAA;AACnC,QAAA,OAAO,IAAA;AAAA,MACT;AAGA,MAAA,MAAM,sBAAA,GAAyB,MAAA,CAAO,IAAA,CAAK,YAAA,IAAgB,wBAAA;AAC3D,MAAA,MAAM,MAAA,GAAoB;AAAA,QACxB,WAAA,EAAa,OAAO,IAAA,CAAK,WAAA;AAAA,QACzB,YAAA,EAAc,sBAAA;AAAA,QACd,SAAA,EAAW,MAAA,CAAO,IAAA,CAAK,SAAA,IAAa,IAAA;AAAA,QACpC,SAAA,EAAW;AAAA,OACb;AACA,MAAA,MAAM,SAAA,GAAY,MAAM,aAAA,CAAc,YAAY;AAChD,QAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,UAAA,OAAO,KAAA;AACT,QAAA,MAAM,OAAA,CAAQ,cAAA,CAAe,MAAA,CAAO,WAAW,CAAA;AAC/C,QAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,UAAA,OAAO,KAAA;AACT,QAAA,MAAM,OAAA,CAAQ,gBAAgB,sBAAsB,CAAA;AACpD,QAAA,OAAO,iBAAA,KAAsB,UAAA;AAAA,MAC/B,CAAC,CAAA;AACD,MAAA,IAAI,CAAC,aAAa,iBAAA,KAAsB,UAAA;AACtC,QAAA,OAAO,IAAA;AACT,MAAA,cAAA,GAAiB,KAAA;AACjB,MAAA,MAAA,CAAO,MAAM,8BAA8B,CAAA;AAC3C,MAAA,KAAA,MAAW,MAAM,SAAA,EAAW;AAC1B,QAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,UAAA,OAAO,IAAA;AACT,QAAA,EAAA,CAAG,MAAM,CAAA;AAAA,MACX;AACA,MAAA,OAAO,iBAAA,KAAsB,aAAa,MAAA,GAAS,IAAA;AAAA,IACrD,SACO,KAAA,EAAO;AACZ,MAAA,IAAI,iBAAA,KAAsB,UAAA;AACxB,QAAA,OAAO,IAAA;AAGT,MAAA,MAAA,CAAO,KAAA,CAAM,kCAAA,EAAoC,EAAE,KAAA,EAAO,CAAA;AAC1D,MAAA,OAAO,IAAA;AAAA,IACT,CAAA,SACA;AACE,MAAA,YAAA,CAAa,SAAS,CAAA;AAAA,IACxB;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,IAAI,UAAA,GAAa;AAAE,MAAA,OAAO,UAAA;AAAA,IAAW,CAAA;AAAA,IACrC,IAAI,cAAA,GAAiB;AAAE,MAAA,OAAO,cAAA;AAAA,IAAe,CAAA;AAAA,IAC7C,OAAA;AAAA,IACA,MAAM,UAAU,MAAA,EAAmB;AACjC,MAAA,MAAM,qBAAqB,iBAAA,EAAkB;AAC7C,MAAA,cAAA,GAAiB,KAAA;AACjB,MAAA,MAAM,kBAAA,GAAqB,UAAA;AAC3B,MAAA,MAAM,OAAA,GAAU,cAAc,YAAY;AACxC,QAAA,IAAI,kBAAA,KAAuB,UAAA;AACzB,UAAA;AAEF,QAAA,MAAM,QAAQ,KAAA,EAAM;AACpB,QAAA,IAAI,kBAAA,KAAuB,UAAA;AACzB,UAAA;AACF,QAAA,MAAM,OAAA,CAAQ,cAAA,CAAe,MAAA,CAAO,WAAW,CAAA;AAC/C,QAAA,IAAI,MAAA,CAAO,gBAAgB,kBAAA,KAAuB,UAAA;AAChD,UAAA,MAAM,OAAA,CAAQ,eAAA,CAAgB,MAAA,CAAO,YAAY,CAAA;AAAA,MACrD,CAAC,CAAA;AAED,MAAA,kBAAA,EAAoB,KAAA,EAAM;AAC1B,MAAA,MAAM,OAAA;AAAA,IACR,CAAA;AAAA,IACA,MAAM,KAAA,GAAQ;AACZ,MAAA,MAAM,qBAAqB,iBAAA,EAAkB;AAC7C,MAAA,MAAM,OAAA,GAAU,aAAA,CAAc,MAAM,OAAA,CAAQ,OAAO,CAAA;AACnD,MAAA,kBAAA,EAAoB,KAAA,EAAM;AAC1B,MAAA,MAAM,OAAA;AAAA,IACR,CAAA;AAAA,IACA,iBAAiB,QAAA,EAAuC;AACtD,MAAA,SAAA,CAAU,KAAK,QAAQ,CAAA;AACvB,MAAA,OAAO,MAAM;AACX,QAAA,MAAM,GAAA,GAAM,SAAA,CAAU,OAAA,CAAQ,QAAQ,CAAA;AACtC,QAAA,IAAI,GAAA,IAAO,CAAA;AACT,UAAA,SAAA,CAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MAC3B,CAAA;AAAA,IACF;AAAA,GACF;AACF;AA0BA,SAAS,iBAAiB,IAAA,EAAwB;AAChD,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,IAAQ,YAAY,IAAA,EAAM;AACjE,IAAA,OAAQ,IAAA,CAA6B,MAAA;AAAA,EACvC;AACA,EAAA,OAAO,IAAA;AACT;;;ACxcA,IAAA,aAAA,GAAA;AAAA,EACE,sBAAA,EAA0B,wBAAA;AAAA,EAC1B,iBAAA,EAAqB,iBAAA;AAAA,EACrB,qBAAA,EAAyB,yBAAA;AAAA,EACzB,sBAAA,EAA0B,mCAAA;AAAA,EAC1B,mBAAA,EAAuB,kBAAA;AAAA,EACvB,kBAAA,EAAsB,oBAAA;AAAA,EACtB,0BAAA,EAA8B,2BAAA;AAAA,EAC9B,4BAAA,EAAgC,2CAAA;AAAA,EAChC,wBAAA,EAA4B,gEAAA;AAAA,EAC5B,4BAAA,EAAgC,yEAAA;AAAA,EAChC,qBAAA,EAAyB,kCAAA;AAAA,EACzB,kCAAA,EAAsC,4BAAA;AAAA,EACtC,iBAAA,EAAqB;AACvB,CAAA;;;ACdA,IAAA,aAAA,GAAA;AAAA,EACE,sBAAA,EAA0B,8GAAA;AAAA,EAC1B,iBAAA,EAAqB,kGAAA;AAAA,EACrB,qBAAA,EAAyB,gEAAA;AAAA,EACzB,sBAAA,EAA0B,sIAAA;AAAA,EAC1B,mBAAA,EAAuB,0EAAA;AAAA,EACvB,kBAAA,EAAsB,0EAAA;AAAA,EACtB,0BAAA,EAA8B,4FAAA;AAAA,EAC9B,4BAAA,EAAgC,oKAAA;AAAA,EAChC,wBAAA,EAA4B,yMAAA;AAAA,EAC5B,4BAAA,EAAgC,oMAAA;AAAA,EAChC,qBAAA,EAAyB,sFAAA;AAAA,EACzB,kCAAA,EAAsC,4FAAA;AAAA,EACtC,iBAAA,EAAqB;AACvB,CAAA;;;ACdA,IAAA,aAAA,GAAA;AAAA,EACE,sBAAA,EAA0B,sCAAA;AAAA,EAC1B,iBAAA,EAAqB,0BAAA;AAAA,EACrB,qBAAA,EAAyB,oDAAA;AAAA,EACzB,sBAAA,EAA0B,wDAAA;AAAA,EAC1B,mBAAA,EAAuB,gCAAA;AAAA,EACvB,kBAAA,EAAsB,kDAAA;AAAA,EACtB,0BAAA,EAA8B,kDAAA;AAAA,EAC9B,4BAAA,EAAgC,mEAAA;AAAA,EAChC,wBAAA,EAA4B,+FAAA;AAAA,EAC5B,4BAAA,EAAgC,qIAAA;AAAA,EAChC,qBAAA,EAAyB,6DAAA;AAAA,EACzB,kCAAA,EAAsC,kDAAA;AAAA,EACtC,iBAAA,EAAqB;AACvB,CAAA;;;ACoBO,IAAM,UAAA,GAAaA,IAAAA,CAAK,IAAA,CAAK,mBAAA,CAAyC;AAAA,EAC3E,OAAA,EAAS,aAAA;AAAA,EACT,OAAA,EAAS,aAAA;AAAA,EACT,OAAA,EAAS;AACX,CAAC,CAAA;AC/BD,IAAM,gBAAA,uBAAuB,OAAA,EAAiC;AASvD,SAAS,yBAAyB,IAAA,EAAoB;AAC3D,EAAA,KAAA,MAAW,IAAA,IAAQ,gBAAA,CAAiB,GAAA,CAAI,IAAI,KAAK,EAAC;AAChD,IAAA,IAAA,EAAK;AACP,EAAA,gBAAA,CAAiB,OAAO,IAAI,CAAA;AAC9B;AAsCO,SAAS,qBAAqB,MAAA,EAGlC;AAaD,EAAA,eAAe,KAAwE,MAAA,EAAsB;AAC3G,IAAA,IAAI,MAAA,CAAO,OAAA;AACT,MAAA,MAAM,MAAA,CAAO,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,KAAK,MAAM,CAAA;AAChD,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO;AAAA,IACL,YAAA,CAAa,OAAA,GAAiC,EAAC,EAAG;AAChD,MAAA,IAAI,CAAC,OAAA,CAAQ,OAAA,IAAW,OAAO,MAAA,KAAW,WAAA;AACxC,QAAA,OAAO,MAAM;AAAA,QAAC,CAAA;AAChB,MAAA,MAAM,MAAA,GAAS,MAAA;AACf,MAAA,IAAI,IAAA,GAAO,KAAA;AACX,MAAA,MAAM,UAAA,GAAa,CAAC,KAAA,KAA+B;AACjD,QAAA,IAAI,MAAM,SAAA,IAAa,IAAA;AACrB,UAAA;AACF,QAAA,IAAA,GAAO,IAAA;AAEP,QAAA,KAAK,OAAO,GAAA,CAAI,IAAA,CAAK,aAAa,EAAC,EAAG,EAAE,OAAA,EAAS,EAAE,SAAA,EAAW,IAAA,EAAM,aAAa,KAAA,EAAM,EAAG,CAAA,CAAE,IAAA,CAAK,CAAC,MAAA,KAAW;AAC3G,UAAA,IAAI,CAAC,MAAA,CAAO,OAAA;AACV,YAAAA,IAAAA,CAAK,OAAO,IAAA,CAAK,qCAAA,EAAuC,EAAE,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,IAAA,EAAM,CAAA;AAAA,QAC5F,CAAC,EAAE,KAAA,CAAM,MAAMA,KAAK,MAAA,CAAO,IAAA,CAAK,4CAA4C,CAAC,CAAA;AAAA,MAC/E,CAAA;AACA,MAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,UAAU,CAAA;AAC9C,MAAA,MAAM,WAAW,gBAAA,CAAiB,GAAA,CAAI,OAAO,IAAI,CAAA,wBAAS,GAAA,EAAgB;AAC1E,MAAA,MAAM,OAAO,MAAM;AACjB,QAAA,MAAA,CAAO,mBAAA,CAAoB,YAAY,UAAU,CAAA;AACjD,QAAA,QAAA,CAAS,OAAO,IAAI,CAAA;AACpB,QAAA,IAAI,CAAC,QAAA,CAAS,IAAA;AACZ,UAAA,gBAAA,CAAiB,MAAA,CAAO,OAAO,IAAI,CAAA;AAAA,MACvC,CAAA;AACA,MAAA,QAAA,CAAS,IAAI,IAAI,CAAA;AACjB,MAAA,gBAAA,CAAiB,GAAA,CAAI,MAAA,CAAO,IAAA,EAAM,QAAQ,CAAA;AAC1C,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,YAAA,EAAc,MAAM,MAAA,CAAO,GAAA,CAAI,KAAK,YAAA,EAAa;AAAA,IACjD,eAAe,CAAC,KAAA,KAA+D,OAAO,GAAA,CAAI,IAAA,CAAK,cAAc,KAAK,CAAA;AAAA,IAClH,KAAA,EAAO,OAAO,KAAA,KAA6B;AACzC,MAAA,MAAM,MAAA,GAAS,KAAA,CAAM,MAAA,KAAW,MAAA,GAC5B,MAAM,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,aAAA,CAAc,EAAE,QAAA,EAAU,KAAA,CAAM,UAAA,EAAY,UAAU,KAAA,CAAM,QAAA,EAAU,OAAA,EAAS,KAAA,CAAM,OAAA,EAAS,cAAA,EAAgB,KAAA,CAAM,cAAA,EAAgB,CAAA,GAC1J,KAAA,CAAM,MAAA,KAAW,OAAA,IAAW,KAAA,CAAM,MAAA,KAAW,KAAA,GAC3C,MAAM,OAAO,GAAA,CAAI,IAAA,CAAK,YAAA,CAAa,EAAE,UAAA,EAAY,KAAA,CAAM,UAAA,EAAY,IAAA,EAAM,MAAM,IAAA,IAAQ,EAAA,EAAI,OAAA,EAAS,KAAA,CAAM,OAAA,EAAS,cAAA,EAAgB,KAAA,CAAM,cAAA,EAAgB,CAAA,GACzJ,MAAM,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,EAAE,UAAA,EAAY,MAAM,UAAA,EAAY,QAAA,EAAU,KAAA,CAAM,QAAA,EAAU,SAAS,KAAA,CAAM,OAAA,EAAS,cAAA,EAAgB,KAAA,CAAM,gBAAgB,CAAA;AAC1J,MAAA,OAAO,KAAK,MAAM,CAAA;AAAA,IACpB,CAAA;AAAA,IACA,QAAA,EAAU,OAAO,KAAA,KAA4B,IAAA,CAAK,MAAM,OAAO,GAAA,CAAI,IAAA,CAAK,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,IACvF,OAAA,EAAS,CAAC,UAAA,EAAoB,OAAA,KAAmC,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,EAAE,UAAA,EAAY,GAAG,OAAA,EAAS,CAAA;AAAA,IACnH,oBAAA,EAAsB,CAAC,UAAA,EAAoB,OAAA,KAAmC,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,oBAAA,CAAqB,EAAE,UAAA,EAAY,GAAG,OAAA,EAAS,CAAA;AAAA,IAC7I,oBAAA,EAAsB,CAAC,KAAA,EAAe,WAAA,KAAwB,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,oBAAA,CAAqB,EAAE,KAAA,EAAO,WAAA,EAAa,CAAA;AAAA,IACzH,eAAe,CAAC,KAAA,KAA2F,OAAO,GAAA,CAAI,IAAA,CAAK,cAAc,KAAK,CAAA;AAAA,IAC9I,cAAA,EAAgB,CAAC,QAAA,EAAkB,SAAA,KAAsB,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,cAAA,CAAe,EAAE,QAAA,EAAU,SAAA,EAAW;AAAA,GACjH;AACF;AC5GA,IAAM,kBAAA,GAAqB;AAAA,EACzB,aAAA,EAAe,SAAA;AAAA,EACf,OAAA,EAAS,SAAA;AAAA,EACT,YAAA,EAAc,SAAA;AAAA,EACd,YAAA,EAAc,SAAA;AAAA,EACd,SAAA,EAAW,SAAA;AAAA,EACX,SAAA,EAAW,SAAA;AAAA,EACX,iBAAA,EAAmB,SAAA;AAAA,EACnB,oBAAA,EAAsB,SAAA;AAAA,EACtB,iBAAA,EAAmB,SAAA;AAAA,EACnB,eAAA,EAAiB,SAAA;AAAA,EACjB,YAAA,EAAc,SAAA;AAAA,EACd,OAAA,EAAS;AACX,CAAA;AAqBO,IAAM,iBAAA,GAAoBA,IAAAA,CAAK,KAAA,CAAM,iBAAA,CAAkB,cAAc,kBAAkB;;;AC0B9F,IAAM,eAAA,GAAkB,GAAA;AACxB,IAAM,mBAAA,GAAsB,gBAAA;AAC5B,IAAM,uBAAuB,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,KAAK,OAAO,CAAA;AAC5E,IAAM,gBAAA,GAAmB;AAAA,EACvB,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,EAC7C,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,KAAK,YAAY,CAAA;AAAA,EACpD,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,KAAK,aAAa,CAAA;AAAA,EACrD,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,KAAK,oBAAoB,CAAA;AAAA,EAC5D,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,KAAK,oBAAoB,CAAA;AAAA,EAC5D,WAAA,CAAY,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,KAAK,QAAQ;AAClD,CAAA;AACA,IAAM,sBAAA,GAAyB,MAAA;AAC/B,IAAM,mBAAA,0BAA6B,oCAAoC,CAAA;AAWvE,IAAM,YAAA,GAAe;AAAA;AAAA,EAEnB,MAAA,EAAQ,wBAAA;AAAA;AAAA,EAER,cAAA,EAAgB;AAClB,CAAA;AA2BO,SAAS,gBACd,QAAA,EACsB;AAEtB,EAAA,MAAM,KAAA,GAMF;AAAA,IACF,MAAA,EAAQ,IAAA;AAAA,IACR,SAAA,EAAW,IAAA;AAAA,IACX,YAAA,EAAc,MAAA;AAAA,IACd,SAAA,EAAW,MAAA;AAAA,IACX,SAAA,sBAAe,GAAA;AAAI,GACrB;AAEA,EAAA,MAAM,IAAA,GAAsB;AAAA,IAC1B,MAAM,UAAU,MAAA,EAAQ;AAAE,MAAA,MAAM,KAAA,CAAM,YAAA,EAAc,SAAA,CAAU,MAAM,CAAA;AAAA,IAAE,CAAA;AAAA,IACtE,MAAM,KAAA,GAAQ;AAAE,MAAA,MAAM,KAAA,CAAM,cAAc,KAAA,EAAM;AAAA,IAAE,CAAA;AAAA,IAClD,iBAAiB,QAAA,EAAU;AACzB,MAAA,OAAO,KAAA,CAAM,YAAA,EAAc,gBAAA,CAAiB,QAAQ,MAAM,MAAM;AAAA,MAAC,CAAA,CAAA;AAAA,IACnE;AAAA,GACF;AAEA,EAAA,IAAI,WAAA,GAA+C,IAAA;AACnD,EAAA,MAAM,SAAA,GAAgC;AAAA,IACpC,MAAM,KAAK,MAAA,EAAQ;AAGjB,MAAA,IAAI,WAAA;AACF,QAAA,OAAO,WAAA;AAET,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,OAAO,GAAA;AAAA,UACL,iBAAA,CAAkB,YAAA;AAAA,UAClB,WAAW,8BAA8B;AAAA,SAC3C;AAAA,MACF;AACA,MAAA,WAAA,GAAA,CAAe,YAAsC;AACnD,QAAA,IAAI,aAAA;AACJ,QAAA,IAAI;AACF,UAAA,aAAA,GAAgB,sBAAsB,MAAM,CAAA;AAC5C,UAAA,MAAM,gBAAA,GAAmB,iBAAA,CAAkB,MAAA,EAAQ,aAAa,CAAA;AAChE,UAAA,MAAM,aAAA,GAAgB,cAAA,CAAe,QAAA,EAAU,MAAA,EAAQ,kBAAkB,aAAa,CAAA;AAGtF,UAAA,KAAA,CAAM,MAAA,GAAS,MAAA;AACf,UAAA,KAAA,CAAM,SAAA,GAAY,aAAA;AAClB,UAAA,KAAA,CAAM,YAAA,GAAe,gBAAA;AACrB,UAAA,KAAA,CAAM,SAAA,GAAY,aAAA;AAClB,UAAA,OAAO,GAAG,KAAA,CAAS,CAAA;AAAA,QACrB,SACO,KAAA,EAAO;AAEZ,UAAA,aAAA,EAAe,OAAA,EAAQ;AACvB,UAAA,KAAA,CAAM,MAAA,GAAS,IAAA;AACf,UAAA,KAAA,CAAM,SAAA,GAAY,IAAA;AAClB,UAAA,KAAA,CAAM,YAAA,GAAe,MAAA;AACrB,UAAA,KAAA,CAAM,SAAA,GAAY,MAAA;AAClB,UAAA,OAAO,GAAA;AAAA,YACL,iBAAA,CAAkB,YAAA;AAAA,YAClB,UAAA,CAAW,uBAAA,EAAyB,EAAE,MAAA,EAAQ,EAAE,OAAO,MAAA,CAAO,KAAK,CAAA,EAAE,EAAG,CAAA;AAAA,YACxE;AAAA,WACF;AAAA,QACF;AAAA,MACF,CAAA,GAAG;AACH,MAAA,IAAI;AACF,QAAA,OAAO,MAAM,WAAA;AAAA,MACf,CAAA,SACA;AACE,QAAA,WAAA,GAAc,IAAA;AAAA,MAChB;AAAA,IACF,CAAA;AAAA,IACA,MAAM,KAAA,GAAQ;AACZ,MAAA,wBAAA,CAAyB,IAAI,CAAA;AAC7B,MAAA,KAAA,CAAM,UAAU,KAAA,EAAM;AACtB,MAAA,KAAA,CAAM,WAAW,OAAA,EAAQ;AACzB,MAAA,KAAA,CAAM,MAAA,GAAS,IAAA;AACf,MAAA,KAAA,CAAM,SAAA,GAAY,IAAA;AAClB,MAAA,KAAA,CAAM,YAAA,GAAe,MAAA;AACrB,MAAA,KAAA,CAAM,SAAA,GAAY,MAAA;AAAA,IACpB,CAAA;AAAA,IACA,IAAI,MAAA,GAAS;AAAE,MAAA,OAAO,MAAM,MAAA,GAASA,IAAAA,CAAK,SAAS,uBAAA,CAAwB,KAAA,CAAM,MAAM,CAAA,GAAI,IAAA;AAAA,IAAK,CAAA;AAAA,IAChG,IAAI,aAAA,GAAgB;AAAE,MAAA,OAAO,MAAM,SAAA,KAAc,IAAA;AAAA,IAAK,CAAA;AAAA,IACtD,IAAA;AAAA,IACA,gBAAgB,QAAA,EAAU;AACxB,MAAA,KAAA,CAAM,SAAA,CAAU,IAAI,QAAQ,CAAA;AAC5B,MAAA,OAAO,MAAM;AACX,QAAA,KAAA,CAAM,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,MACjC,CAAA;AAAA,IACF;AAAA,GACF;AAIA,EAAA,OAAO,IAAI,KAAA,CAAM,EAAC,EAAG;AAAA,IACnB,GAAA,CAAI,SAAS,QAAA,EAAU;AAGrB,MAAA,IAAI,OAAO,QAAA,KAAa,QAAA;AACtB,QAAA,OAAO,MAAA;AAOT,MAAA,IAAI,YAAY,SAAA,EAAW;AACzB,QAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,SAAA,EAAW,QAAQ,CAAA;AAAA,MACxC;AAOA,MAAA,OAAO,oBAAA,CAAqB,KAAA,EAAO,CAAC,QAAQ,CAAC,CAAA;AAAA,IAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,GAKD,CAAA;AACH;AAyBA,IAAM,kBAAA,GAAqB,WAAA,CAAY,MAAA,CAAO,EAAE,GAAA,EAAK,WAAA,CAAY,GAAA,EAAK,OAAA,EAAS,WAAA,CAAY,OAAA,EAAS,EAAA,EAAI,WAAA,CAAY,IAAI,CAAA;AAwCjH,IAAM,SAAA,GAAY,IAAI,KAAA,CAAM,eAAA,CAAgB,kBAAkB,CAAA,EAA2C;AAAA,EAC9G,GAAA,CAAI,MAAA,EAAQ,QAAA,EAAU,QAAA,EAAU;AAC9B,IAAA,IAAI,QAAA,KAAa,QAAA;AACf,MAAA,OAAO,eAAA;AACT,IAAA,IAAI,QAAA,KAAa,cAAA;AACf,MAAA,OAAO,YAAA;AACT,IAAA,IAAI,QAAA,KAAa,mBAAA;AACf,MAAA,OAAO,oBAAA;AACT,IAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAkB,QAAA,EAAU,QAAQ,CAAA;AAAA,EACzD;AACF,CAAC;AAkBD,SAAS,oBAAA,CACP,OACA,IAAA,EACS;AACT,EAAA,OAAO,IAAI,KAAA,CAAM,MAAM,MAAA,EAAW;AAAA,IAChC,GAAA,CAAI,IAAI,QAAA,EAAU;AAChB,MAAA,IAAI,OAAO,QAAA,KAAa,QAAA;AACtB,QAAA,OAAO,MAAA;AACT,MAAA,OAAO,qBAAqB,KAAA,EAAO,CAAC,GAAG,IAAA,EAAM,QAAQ,CAAC,CAAA;AAAA,IACxD,CAAA;AAAA,IACA,KAAA,CAAM,EAAA,EAAI,QAAA,EAAU,IAAA,EAAM;AACxB,MAAA,OAAO,sBAAsB,KAAA,CAAM,SAAA,EAAW,IAAA,EAAM,IAAA,EAAM,MAAM,SAAS,CAAA;AAAA,IAC3E;AAAA,GACD,CAAA;AACH;AAGA,eAAe,qBAAA,CAAsB,SAAA,EAAoB,IAAA,EAAgB,IAAA,EAAiB,SAAA,EAA+D;AACvJ,EAAA,MAAM,MAAA,GAA+B,EAAE,SAAA,EAAWA,IAAAA,CAAK,OAAA,CAAQ,GAAA,EAAI,CAAE,SAAA,IAAaA,IAAAA,CAAK,EAAA,CAAG,IAAA,EAAK,EAAG,SAAA,EAAW,KAAK,IAAA,CAAK,GAAG,CAAA,EAAG,SAAA,EAAW,IAAA,CAAK,GAAA,EAAI,EAAG,QAAA,EAAU,CAAA,EAAG,OAAA,EAAS,KAAA,EAAO,QAAA,EAAU,CAAA,EAAE;AAC7L,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,CAAC,CAAA,IAAK,OAAO,IAAA,CAAK,CAAC,CAAA,KAAM,QAAA,GAAW,IAAA,CAAK,CAAC,CAAA,GAA+B,EAAC;AAChG,EAAA,MAAM,eAAA,GAAkB,SAAS,OAAA,IAAW,OAAO,SAAS,OAAA,KAAY,QAAA,GAAW,QAAA,CAAS,OAAA,GAAU,EAAC;AACvG,EAAA,MAAM,OAAA,GAAU,EAAE,GAAG,QAAA,EAAU,OAAA,EAAS,EAAE,GAAG,eAAA,EAAiB,CAAC,mBAAmB,GAAG,MAAA,EAAO,EAAE;AAC9F,EAAA,IAAI,MAAA,GAAS,MAAM,aAAA,CAAc,SAAA,EAAW,IAAA,EAAM,CAAC,IAAA,CAAK,CAAC,CAAA,EAAG,OAAO,CAAC,CAAA;AACpE,EAAA,MAAA,CAAO,QAAA,GAAW,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,EAAI,GAAI,OAAO,SAAS,CAAA;AAC3D,EAAA,MAAA,CAAO,OAAA,GAAU,EAAE,MAAA,IAAU,OAAO,WAAW,QAAA,IAAY,SAAA,IAAa,MAAA,IAAU,MAAA,CAAO,OAAA,KAAY,KAAA,CAAA;AACrG,EAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,WAAW,MAAA,IAAU,MAAA,CAAO,KAAA,IAAS,OAAO,MAAA,CAAO,KAAA,KAAU,QAAA,IAAY,MAAA,IAAU,OAAO,KAAA,EAAO;AAC3I,IAAA,MAAA,CAAO,SAAA,GAAY,MAAA,CAAO,MAAA,CAAO,KAAA,CAAM,IAAI,CAAA;AAC3C,IAAA,IAAI,aAAa,MAAA,CAAO,KAAA,IAAS,OAAO,MAAA,CAAO,KAAA,CAAM,YAAY,QAAA,EAAU;AACzE,MAAA,MAAM,QAAQ,MAAA,CAAO,KAAA;AACrB,MAAA,MAAM,UAAA,GAAa,KAAA,CAAM,OAAA,IAAW,kBAAA,CAAmB,IAAA,CAAK,MAAM,OAAO,CAAA,GAAI,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,SAAA;AACpG,MAAA,MAAA,CAAO,SAAA,GAAY,UAAA;AACnB,MAAA,MAAM,IAAA,GAAOA,IAAAA,CAAK,KAAA,CAAM,QAAA,CAAS,EAAE,GAAG,KAAA,EAAO,SAAA,EAAW,KAAA,CAAM,GAAA,EAAI,EAAG,UAAU,CAAA;AAC/E,MAAA,IAAI,KAAA,CAAM,KAAA,IAAS,OAAO,KAAA,CAAM,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,EAAG;AACjF,QAAA,MAAM,WAAoC,EAAC;AAC3C,QAAA,KAAA,MAAW,GAAA,IAAO,CAAC,UAAA,EAAY,QAAA,EAAU,iBAAiB,WAAA,EAAa,OAAA,EAAS,YAAA,EAAc,aAAa,CAAA,EAAG;AAC5G,UAAA,MAAM,KAAA,GAAiB,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,OAAO,GAAG,CAAA;AACnD,UAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,OAAO,KAAA,KAAU,QAAA;AAChD,YAAA,QAAA,CAAS,GAAG,CAAA,GAAIA,IAAAA,CAAK,QAAA,CAAS,sBAAA,CAAuB,OAAO,GAAG,CAAA;AAAA,QACnE;AACA,QAAA,IAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,MAAA;AACxB,UAAA,IAAA,CAAK,KAAA,GAAQ,QAAA;AAAA,MACjB;AACA,MAAAA,IAAAA,CAAK,OAAO,KAAA,CAAM,oBAAA,EAAsB,EAAE,OAAA,EAAS,IAAA,CAAK,SAAS,SAAA,EAAW,MAAA,CAAO,WAAW,SAAA,EAAW,MAAA,CAAO,WAAW,MAAA,EAAQ,MAAA,CAAO,QAAQ,KAAA,EAAO,MAAA,CAAO,OAAO,CAAA;AACvK,MAAA,MAAA,GAAS,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAK;AAAA,IACpC;AAAA,EACF;AACA,EAAA,KAAA,MAAW,QAAA,IAAY,SAAA,IAAa,EAAC,EAAG;AACtC,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAoB,SAAS,MAAA,CAAO,MAAA,CAAO,EAAE,GAAG,MAAA,EAAQ,CAAC,CAAA;AAC/D,MAAA,IAAI,QAAA,YAAoB,OAAA;AACtB,QAAA,KAAK,QAAA,CAAS,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,IAChC,CAAA,CAAA,MACM;AAAA,IAAqB;AAAA,EAC7B;AACA,EAAA,OAAO,MAAA;AACT;AAeA,eAAe,aAAA,CACb,SAAA,EACA,IAAA,EACA,IAAA,EACkB;AAClB,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,eAAA,EAAiB,UAAA,CAAW,0BAA0B,CAAC,CAAA;AAAA,EACtF;AAGA,EAAA,IAAI,MAAA,GAAkB,SAAA;AACtB,EAAA,KAAA,MAAW,WAAW,IAAA,EAAM;AAC1B,IAAA,IAAA,CAAK,OAAO,MAAA,KAAW,QAAA,IAAY,WAAW,IAAA,KAAS,OAAO,WAAW,UAAA,EAAY;AACnF,MAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,OAAA,EAAS,UAAA,CAAW,mBAAmB,CAAC,CAAA;AAAA,IACvE;AACA,IAAA,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAkB,OAAO,CAAA;AAAA,EAChD;AACA,EAAA,IAAI,OAAO,WAAW,UAAA,EAAY;AAChC,IAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,OAAA,EAAS,UAAA,CAAW,mBAAmB,CAAC,CAAA;AAAA,EACvE;AAEA,EAAA,IAAI;AACF,IAAA,OAAO,MAAO,MAAA,CAAiD,GAAG,IAAI,CAAA;AAAA,EACxE,SACO,KAAA,EAAO;AACZ,IAAA,OAAO,eAAe,KAAK,CAAA;AAAA,EAC7B;AACF;AAgBA,SAAS,iBAAA,CAAkB,QAAyB,SAAA,EAAkE;AACpH,EAAA,IAAI,CAAC,MAAA,CAAO,IAAA;AACV,IAAA,OAAO,MAAA;AAET,EAAA,MAAM,OAAA,GAAU,WAAW,cAAA,IAAkB,MAAA,CAAO,SAAS,UAAA,CAAW,KAAA,CAAM,KAAK,UAAU,CAAA;AAI7F,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,IAAA,CAAK,OAAA,IAAW,gCAAA,EAAiC;AACxE,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,wBAAwB,EAAE,CAAA;AACjE,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,WAAA,IAAe,oBAAA;AAC/C,EAAA,MAAM,UAAA,GAAa,CAAA,EAAG,OAAO,CAAA,EAAG,WAAA,CAAY,UAAA,CAAW,GAAG,CAAA,GAAI,WAAA,GAAc,CAAA,CAAA,EAAI,WAAW,CAAA,CAAE,CAAA,CAAA;AAG7F,EAAA,MAAM,YAAA,GAA6B,OAAO,KAAA,EAAO,IAAA,KAAS;AACxD,IAAA,MAAM,UAAA,GAAa,OAAO,MAAA,CAAO,OAAA,KAAY,UAAA,GAAa,MAAM,MAAA,CAAO,OAAA,EAAQ,GAAI,MAAA,CAAO,OAAA,IAAW,EAAC;AACtG,IAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,UAAU,CAAA;AACtC,IAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA;AAChC,MAAA,OAAA,CAAQ,GAAA,CAAI,iBAAA,EAAmBA,IAAAA,CAAK,IAAA,CAAK,iBAAiB,CAAA;AAC5D,IAAA,OAAA,CAAQ,GAAA,CAAI,kBAAA,EAAoB,MAAA,CAAO,UAAA,IAAc,mBAAmB,CAAA;AACxE,IAAA,IAAI,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CAAE,OAAA,CAAQ,CAAC,KAAA,EAAO,IAAA,KAAS,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,KAAK,CAAC,CAAA;AAC5E,IAAA,OAAO,QAAQ,KAAA,EAAO,EAAE,GAAG,IAAA,EAAM,SAAS,CAAA;AAAA,EAC5C,CAAA;AACA,EAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,OAAA,EAAS,UAAA,EAAY,cAAc,MAAA,CAAO,IAAA,CAAK,cAAA,GAAiB,MAAA,GAAY,MAAA,CAAO,IAAA,CAAK,eAAA,EAAiB,MAAA,CAAO,KAAK,gBAAgB,CAAA;AACxK,EAAA,IAAI,MAAA,CAAO,KAAK,gBAAA,EAAkB;AAChC,IAAA,OAAA,CAAQ,gBAAA,CAAiB,MAAA,CAAO,IAAA,CAAK,gBAAgB,CAAA;AAAA,EACvD;AACA,EAAA,OAAO,OAAA;AACT;AAcA,SAAS,sBAAsB,MAAA,EAAsD;AACnF,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA;AACV,IAAA,OAAO,MAAA;AACT,EAAA,MAAM,YAAY,MAAA,CAAO,KAAA,IAAS,UAAA,CAAW,KAAA,CAAM,KAAK,UAAU,CAAA;AAClE,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,wBAAwB,EAAE,CAAA;AACjE,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,SAAA,CAAU,eAAA,IAAmB,oBAAA;AACjD,EAAA,MAAM,cAAA,GAAiB,CAAA,EAAG,OAAO,CAAA,EAAG,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,IAAA,GAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA,CAAA;AAC5E,EAAA,OAAO,MAAA,CAAO,SAAA,CAAU,MAAA,CAAO,SAAA,CAAU,YAAA,CAAa,EAAE,cAAA,EAAgB,KAAA,EAAO,SAAA,EAAW,iBAAA,EAAmB,MAAA,CAAO,SAAA,CAAU,mBAAmB,CAAA;AACnJ;AAiBA,SAAS,cAAA,CACP,QAAA,EACA,MAAA,EACA,YAAA,EACA,SAAA,EACkD;AAClD,EAAA,MAAM,IAAA,GAAO,IAAI,WAAA,CAA0C,QAAA,EAAU;AAAA,IACnE,KAAK,MAAA,CAAO,OAAA;AAAA,IACZ,OAAA,EAAS,OAAO,OAAA,KAAY;AAC1B,MAAA,MAAM,OAAA,GAAU,MAAM,YAAA,CAAa,MAAA,EAAQ,cAAc,cAAA,CAAe,OAAA,CAAQ,OAAO,CAAA,EAAG,SAAS,CAAA;AAEnG,MAAA,IAAI,OAAO,OAAA,CAAQ,OAAA,CAAQ,gBAAA,KAAqB,QAAA;AAC9C,QAAA,OAAA,CAAQ,wBAAwB,CAAA,GAAI,OAAA,CAAQ,OAAA,CAAQ,gBAAA;AACtD,MAAA,OAAO,OAAA;AAAA,IACT,CAAA;AAAA,IACA,KAAA,EAAO,OAAO,OAAA,EAAS,IAAA,EAAM,OAAA,KAAY;AACvC,MAAA,MAAM,MAAA,GAAS,cAAA,CAAe,OAAA,CAAQ,OAAO,CAAA;AAC7C,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,MAAA,CAAO,SAAS,OAAA,CAAQ,MAAA;AACxB,QAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,QAAA,GAAA,CAAI,MAAA,GAAS,EAAA;AACb,QAAA,GAAA,CAAI,IAAA,GAAO,EAAA;AACX,QAAA,MAAA,CAAO,MAAMA,IAAAA,CAAK,QAAA,CAAS,WAAA,CAAY,GAAA,CAAI,UAAU,CAAA;AAAA,MACvD;AACA,MAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,OAAA,CAAQ,SAAA,KAAc,IAAA,GAAO,EAAE,GAAG,IAAA,EAAM,SAAA,EAAW,IAAA,EAAK,GAAI,IAAA;AACxF,MAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,CAAQ,WAAA;AAChC,MAAA,MAAM,WAAA,GAAc,OAAO,OAAA,KAAY,UAAA,GACnC,YAAY;AACZ,QAAA,MAAM,KAAA,GAAiB,MAAM,OAAA,EAAQ;AACrC,QAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,SAAY,KAAA,GAAQ,IAAA;AAAA,MACpE,CAAA,GACE,MAAA;AACJ,MAAA,MAAM,QAAA,GAAW,MAAM,gBAAA,CAAiB,OAAA,EAAS,aAAa,MAAA,EAAQ,OAAA,CAAQ,OAAA,CAAQ,WAAA,KAAgB,KAAA,GAAQ,MAAA,GAAY,YAAA,EAAc,SAAA,EAAW,QAAQ,WAAW,CAAA;AACtK,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,MAAA,CAAO,SAAS,QAAA,CAAS,MAAA;AACzB,QAAA,MAAM,OAAA,GAAU,SAAS,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA,IAAK,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA;AACzF,QAAA,IAAI,OAAA,IAAW,kBAAA,CAAmB,IAAA,CAAK,OAAO,CAAA;AAC5C,UAAA,MAAA,CAAO,SAAA,GAAY,OAAA;AAAA,MACvB;AACA,MAAA,OAAO,QAAA;AAAA,IACT;AAAA,GACD,CAAA;AACD,EAAA,OAAO,iBAAmE,IAAI,CAAA;AAChF;AAGA,SAAS,eAAe,OAAA,EAAyE;AAC/F,EAAA,MAAM,MAAA,GAAS,QAAQ,mBAAmB,CAAA;AAC1C,EAAA,OAAO,UAAU,OAAO,MAAA,KAAW,QAAA,IAAY,WAAA,IAAe,SAAS,MAAA,GAAiC,MAAA;AAC1G;AAcA,eAAe,YAAA,CACb,MAAA,EACA,YAAA,EACA,SAAA,EACiC;AACjC,EAAA,MAAM,UAAA,GAAa,OAAO,MAAA,CAAO,OAAA,KAAY,UAAA,GAAa,MAAM,MAAA,CAAO,OAAA,EAAQ,GAAI,MAAA,CAAO,OAAA,IAAW,EAAC;AACtG,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,UAAU,CAAA;AACtC,EAAA,OAAA,CAAQ,GAAA,CAAI,gBAAA,EAAkB,MAAA,CAAO,IAAA,EAAM,gBAAgB,KAAK,CAAA;AAChE,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA;AAChC,IAAA,OAAA,CAAQ,GAAA,CAAI,iBAAA,EAAmBA,IAAAA,CAAK,IAAA,CAAK,iBAAiB,CAAA;AAC5D,EAAA,MAAM,WAAA,GAAc,MAAM,YAAA,EAAc,OAAA,CAAQ,cAAA,EAAe;AAC/D,EAAA,OAAO;AAAA,IACL,GAAG,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AAAA,IAC7B,kBAAA,EAAoB,OAAO,UAAA,IAAc,mBAAA;AAAA,IACzC,cAAA,EAAgB,aAAaA,IAAAA,CAAK,OAAA,CAAQ,KAAI,CAAE,SAAA,IAAaA,IAAAA,CAAK,EAAA,CAAG,IAAA,EAAK;AAAA,IAC1E,GAAI,cAAc,EAAE,aAAA,EAAe,UAAU,WAAW,CAAA,CAAA,KAAO;AAAC,GAClE;AACF;AAqBA,eAAe,iBACb,OAAA,EACA,IAAA,EACA,QACA,YAAA,EACA,SAAA,EACA,aACA,WAAA,EACmB;AAGnB,EAAA,MAAM,YAAA,GAAe,YAAA,IAAgB,WAAA,GAAc,OAAA,CAAQ,OAAM,GAAI,MAAA;AACrE,EAAA,MAAM,qBAAqB,YAAA,EAAc,UAAA;AACzC,EAAA,IAAI,WAAA;AACF,IAAA,WAAA,CAAY,QAAA,EAAA;AACd,EAAA,MAAM,WAAW,MAAM,gBAAA,CAAiB,OAAA,EAAS,IAAA,EAAM,QAAQ,SAAS,CAAA;AACxE,EAAA,IAAI,CAAC,YAAA;AACH,IAAA,OAAO,QAAA;AAET,EAAA,eAAe,gBAAgBC,YAAAA,EAAyC;AACtE,IAAA,IAAI,CAAC,YAAA,IAAgB,YAAA,CAAa,MAAA,CAAO,OAAA;AACvC,MAAA,OAAO,QAAA;AACT,IAAA,MAAM,aAAa,YAAA,EAAc,UAAA;AACjC,IAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,YAAA,CAAa,OAAO,CAAA;AAChD,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,wBAAwB,CAAA,IAAK,WAAA,EAAa;AACxD,MAAA,IAAI,CAAC,WAAA;AACH,QAAA,OAAO,QAAA;AACT,MAAA,MAAM,MAAA,GAAS,MAAM,WAAA,EAAY;AACjC,MAAA,IAAI,WAAW,IAAA,IAAQ,YAAA,CAAa,MAAA,CAAO,OAAA,IAAW,cAAc,UAAA,KAAe,UAAA;AACjF,QAAA,OAAO,QAAA;AACT,MAAA,OAAA,CAAQ,OAAO,wBAAwB,CAAA;AACvC,MAAA,IAAI,MAAA;AACF,QAAA,OAAA,CAAQ,GAAA,CAAI,0BAA0B,MAAM,CAAA;AAAA,IAChD;AACA,IAAA,MAAM,YAAA,GAAeA,YAAAA,IAAe,MAAM,YAAA,EAAc,QAAQ,cAAA,EAAe;AAC/E,IAAA,IAAI,cAAc,UAAA,KAAe,UAAA;AAC/B,MAAA,OAAO,QAAA;AACT,IAAA,IAAI,YAAA;AACF,MAAA,OAAA,CAAQ,GAAA,CAAI,eAAA,EAAiB,CAAA,OAAA,EAAU,YAAY,CAAA,CAAE,CAAA;AACvD,IAAA,IAAI,WAAA;AACF,MAAA,WAAA,CAAY,QAAA,EAAA;AACd,IAAA,OAAO,gBAAA,CAAiB,IAAI,OAAA,CAAQ,YAAA,EAAc,EAAE,SAAS,CAAA,EAAG,IAAA,EAAM,MAAA,EAAQ,SAAS,CAAA;AAAA,EACzF;AAGA,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,wBAAwB,EAAE,CAAA;AACjE,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,EAAM,WAAA,IAAe,oBAAA;AAChD,EAAA,MAAM,aAAA,GAAgB,CAAC,GAAG,gBAAA,EAAkB,WAAW,CAAA;AACvD,EAAA,IAAI,aAAA,CAAc,IAAA,CAAK,CAAA,IAAA,KAAQ,IAAI,GAAA,CAAI,GAAG,OAAO,CAAA,EAAG,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAAI,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA,CAAE,CAAA,CAAE,QAAA,KAAa,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,QAAQ,CAAA;AACxI,IAAA,OAAO,QAAA;AACT,EAAA,MAAM,OAAA,GAAmB,SAAS,MAAA,KAAW,GAAA,IAAO,SAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,EAAG,QAAA,CAAS,kBAAkB,CAAA,GACjH,MAAM,SAAS,KAAA,EAAM,CAAE,MAAK,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA,GACnD,MAAA;AACJ,EAAA,MAAM,IAAA,GAAO,WAAW,OAAO,OAAA,KAAY,YAAY,SAAA,IAAa,OAAA,IAAW,OAAA,CAAQ,OAAA,KAAY,KAAA,IAAS,OAAA,IAAW,WAAW,OAAA,CAAQ,KAAA,IAAS,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,IAAY,UAAU,OAAA,CAAQ,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,IAAA,GAAO,MAAA;AACvO,EAAA,IAAI,SAAS,aAAA,IAAiB,WAAA;AAC5B,IAAA,OAAO,eAAA,EAAgB;AACzB,EAAA,IAAI,CAAC,YAAA;AACH,IAAA,OAAO,QAAA;AACT,EAAA,IAAI,SAAS,MAAA,KAAW,GAAA,IAAO,CAAC,MAAM,qBAAqB,QAAQ,CAAA;AACjE,IAAA,OAAO,QAAA;AACT,EAAA,IAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,IAAY,WAAW,OAAA,IAAW,OAAA,CAAQ,KAAA,IAAS,OAAO,OAAA,CAAQ,KAAA,KAAU,QAAA,IAAY,MAAA,IAAU,QAAQ,KAAA,IACvI,OAAO,OAAA,CAAQ,KAAA,CAAM,IAAA,KAAS,QAAA,IAAY,OAAA,CAAQ,KAAA,CAAM,KAAK,UAAA,CAAW,UAAU,CAAA,IAAK,CAAC,0BAA0B,SAAA,CAAU,OAAA,CAAQ,KAAA,CAAM,IAAI,EAAE,OAAA,EAAS;AAC5J,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,IAAI,aAAa,UAAA,KAAe,kBAAA;AAC9B,IAAA,OAAO,QAAA;AAET,EAAA,MAAM,MAAA,GAAS,MAAM,YAAA,CAAa,OAAA,EAAQ;AAC1C,EAAA,IAAI,aAAa,UAAA,KAAe,kBAAA;AAC9B,IAAA,OAAO,QAAA;AACT,EAAA,IAAI,cAAc,MAAA,EAAQ,WAAA;AAE1B,EAAA,IAAI,CAAC,WAAA,IAAe,MAAA,CAAO,IAAA,EAAM,cAAA,IAAkB,aAAa,cAAA,EAAgB;AAC9E,IAAA,IAAI,MAAM,MAAA,CAAO,IAAA,CAAK,cAAA,EAAe,EAAG;AACtC,MAAA,MAAM,qBAAqB,YAAA,CAAa,UAAA;AACxC,MAAA,WAAA,GAAc,MAAM,YAAA,CAAa,OAAA,CAAQ,cAAA,EAAe,IAAK,MAAA;AAC7D,MAAA,IAAI,aAAa,UAAA,KAAe,kBAAA;AAC9B,QAAA,OAAO,QAAA;AAAA,IACX;AAAA,EACF;AACA,EAAA,IAAI,CAAC,WAAA;AACH,IAAA,OAAO,QAAA;AAET,EAAA,OAAO,gBAAgB,WAAW,CAAA;AACpC;AAeA,eAAe,qBAAqB,QAAA,EAAsC;AACxE,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,CAAC,QAAA,CAAS,QAAQ,GAAA,CAAI,cAAc,CAAA,EAAG,QAAA,CAAS,kBAAkB,CAAA;AAC/F,IAAA,OAAO,KAAA;AACT,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAgB,MAAM,QAAA,CAAS,KAAA,GAAQ,IAAA,EAAK;AAClD,IAAA,IAAI,CAAC,QAAQ,OAAO,IAAA,KAAS,YAAY,EAAE,SAAA,IAAa,SAAS,IAAA,CAAK,OAAA,KAAY,SAC7E,EAAE,OAAA,IAAW,SAAS,CAAC,IAAA,CAAK,SAAS,OAAO,IAAA,CAAK,UAAU,QAAA,EAAU;AACxE,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OAAO,YAAA,IAAgB,KAAK,KAAA,IAAS,IAAA,CAAK,MAAM,UAAA,KAAe,GAAA,IAC1D,UAAU,IAAA,CAAK,KAAA,IAAS,OAAO,IAAA,CAAK,KAAA,CAAM,SAAS,QAAA,IACnD,SAAA,IAAa,KAAK,KAAA,IAAS,OAAO,IAAA,CAAK,KAAA,CAAM,OAAA,KAAY,QAAA;AAAA,EAChE,CAAA,CAAA,MACM;AAEJ,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAmBA,eAAe,gBAAA,CACb,OAAA,EACA,IAAA,EACA,MAAA,EACA,SAAA,EACmB;AAGnB,EAAA,MAAM,OAAA,GAAU,WAAW,cAAA,IAAkB,MAAA,CAAO,SAAS,UAAA,CAAW,KAAA,CAAM,KAAK,UAAU,CAAA;AAC7F,EAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,eAAA;AAClC,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAE9D,EAAA,MAAM,aAAa,OAAA,CAAQ,MAAA;AAC3B,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAI,WAAW,OAAA,EAAS;AACtB,MAAA,UAAA,CAAW,KAAA,EAAM;AAAA,IACnB,CAAA,MACK;AACH,MAAA,UAAA,CAAW,gBAAA,CAAiB,SAAS,MAAM,UAAA,CAAW,OAAM,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,IAC/E;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,OAAA,CAAQ,IAAI,OAAA,CAAQ,OAAA,EAAS,EAAE,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,CAAA,EAAG,IAAI,CAAA;AAAA,EAChF,CAAA,SACA;AACE,IAAA,YAAA,CAAa,SAAS,CAAA;AAAA,EACxB;AACF;AAaA,SAAS,eAAe,KAAA,EAAgB;AACtC,EAAA,MAAM,IAAA,GAAO,KAAA,YAAiB,kBAAA,GAAqB,KAAA,GAAQ,KAAA,YAAiB,SAAS,KAAA,CAAM,KAAA,YAAiB,kBAAA,GAAqB,KAAA,CAAM,KAAA,GAAQ,MAAA;AAC/I,EAAA,IAAI,IAAA;AACF,IAAA,OAAO,GAAA,CAAI,kBAAkB,iBAAA,EAAmB,IAAA,CAAK,SAAS,EAAE,aAAA,EAAe,IAAA,CAAK,IAAA,EAAM,CAAA;AAC5F,EAAA,MAAM,QAAA,GAAW,KAAA,YAAiB,0BAAA,GAA6B,KAAA,GAAQ,KAAA,YAAiB,SAAS,KAAA,CAAM,KAAA,YAAiB,0BAAA,GAA6B,KAAA,CAAM,KAAA,GAAQ,MAAA;AACnK,EAAA,IAAI,QAAA;AACF,IAAA,OAAO,IAAI,iBAAA,CAAkB,iBAAA,EAAmB,SAAS,OAAA,EAAS,EAAE,WAAW,QAAA,CAAS,SAAA,EAAW,KAAA,EAAO,QAAA,CAAS,OAAO,UAAA,EAAY,QAAA,CAAS,YAAY,WAAA,EAAa,QAAA,CAAS,aAAa,CAAA;AAChM,EAAA,IAAI,iBAAiB,SAAA,EAAW;AAG9B,IAAA,MAAM,IAAA,GAAgB,KAAA,CAAM,IAAA,IAAQ,OAAO,KAAA,CAAM,IAAA,KAAS,QAAA,IAAY,MAAA,IAAU,KAAA,CAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,IAAA,GAAO,MAAA;AAC/G,IAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,IAAY,SAAA,IAAa,QAAQ,IAAA,CAAK,OAAA,KAAY,KAAA,IAAS,OAAA,IAAW,IAAA,EAAM;AACtG,MAAA,MAAM,MAAA,GAAS,cAAA,CAAe,SAAA,CAAU,IAAA,CAAK,KAAK,CAAA;AAClD,MAAA,IAAI,MAAA,CAAO,OAAA;AACT,QAAA,OAAO,GAAA,CAAI,EAAE,GAAG,MAAA,CAAO,MAAM,UAAA,EAAY,KAAA,CAAM,QAAQ,CAAA;AAAA,IAC3D;AAEA,IAAA,MAAM,MAAA,GAAS,EAAE,QAAA,EAAU,KAAA,CAAM,IAAA,EAAM,QAAQ,KAAA,CAAM,MAAA,EAAQ,IAAA,EAAM,KAAA,CAAM,IAAA,EAAK;AAC9E,IAAA,IAAI,MAAM,MAAA,KAAW,GAAA;AACnB,MAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,iBAAA,EAAmB,KAAA,CAAM,SAAS,MAAM,CAAA;AACvE,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,GAAA,IAAO,KAAA,CAAM,MAAA,KAAW,GAAA;AAC3C,MAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,iBAAA,EAAmB,KAAA,CAAM,SAAS,MAAM,CAAA;AACvE,IAAA,IAAI,MAAM,MAAA,KAAW,GAAA;AACnB,MAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,YAAA,EAAc,KAAA,CAAM,SAAS,MAAM,CAAA;AAClE,IAAA,IAAI,MAAM,MAAA,KAAW,GAAA;AACnB,MAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,SAAA,EAAW,KAAA,CAAM,SAAS,MAAM,CAAA;AAC/D,IAAA,IAAI,MAAM,MAAA,KAAW,GAAA;AACnB,MAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,SAAA,EAAW,KAAA,CAAM,SAAS,MAAM,CAAA;AAC/D,IAAA,OAAO,GAAA,CAAI,iBAAA,CAAkB,YAAA,EAAc,KAAA,CAAM,SAAS,MAAM,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,KAAA,YAAiB,YAAA,IAAgB,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AAChE,IAAA,OAAO,IAAI,iBAAA,CAAkB,OAAA,EAAS,UAAA,CAAW,mBAAmB,GAAG,KAAK,CAAA;AAAA,EAC9E;AACA,EAAA,OAAO,IAAI,iBAAA,CAAkB,aAAA,EAAe,UAAA,CAAW,wBAAwB,GAAG,KAAK,CAAA;AACzF","file":"index.js","sourcesContent":["/**\n * @h-ai/api-client — Token 存储与刷新管理\n *\n * 提供两个内置 Token 存储适配器(httpOnly Cookie / 内存)和一个 Token 管理器\n * (封装存储读写、自动刷新、并发去重、回调通知)。\n *\n * @example 浏览器 SPA\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n *\n * const initialized = await apiClient.init({\n * baseUrl: 'https://api.example.com/api/v1',\n * auth: { storage: apiClient.tokenStorage.httpOnlyCookie() },\n * })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * await apiClient.close()\n * ```\n *\n * @example Node.js 测试\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n *\n * const storage = apiClient.tokenStorage.memory()\n * await storage.setAccessToken('test-access')\n * await storage.clear()\n * ```\n *\n * @module api-client-auth\n */\n\nimport type { TokenPair, TokenStorage } from './api-client-types.js'\nimport { core } from '@h-ai/core'\nimport { z } from 'zod'\n\nconst logger = core.logger.child({ module: 'api-client', scope: 'auth' })\n\n/**\n * httpOnly cookie 模式哨兵值。\n * `apiClient.tokenStorage.httpOnlyCookie()` 的 `getRefreshToken()` 返回此值,\n * 告知 `doRefresh` 应依赖浏览器自动发送的 httpOnly Cookie,\n * 而不是将 refreshToken 写入请求体。\n */\nconst HTTPONLY_COOKIE_SENTINEL = '__httponly_cookie__'\n\n// ─── 内存存储 ─────────────────────────────────────────────────────────────────\n\n/**\n * 创建仅属于本适配器实例的内存凭据存储,页面刷新或进程结束后丢失。\n * SSR 应为每个请求创建客户端和存储实例,避免不同用户共享默认单例。\n * 通过 client.auth 更新已绑定的存储,才能撤销旧代际刷新;直接存储写入不提供这种保证。\n * @returns 两个初始为空的 Token 槽位及清理操作。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const storage = apiClient.tokenStorage.memory()\n * await storage.setAccessToken('test-access')\n * const saved = await storage.getAccessToken()\n * await storage.clear()\n * // saved 为本实例的测试值;不把 Token 写入 Web Storage。\n * ```\n */\nexport function createMemoryTokenStorage(): TokenStorage {\n let accessToken: string | null = null\n let refreshToken: string | null = null\n return {\n async getAccessToken() { return accessToken },\n async setAccessToken(token) { accessToken = token },\n async getRefreshToken() { return refreshToken },\n async setRefreshToken(token) { refreshToken = token },\n async clear() {\n accessToken = null\n refreshToken = null\n },\n }\n}\n\n// ─── httpOnly Cookie 存储 ────────────────────────────────────────────────────\n\n/**\n * 创建浏览器使用的内存 access token 与服务端 httpOnly refresh Cookie 适配器。\n * refresh getter 返回内部哨兵,setter 不修改 Cookie;clear 只清除内存 access token。\n * 刷新请求使用 credentials: include;服务端须实际设置/撤销 Cookie,配置适当的 Secure、SameSite、Path 与跨域凭据策略。\n * HttpOnly 限制脚本直接读取 Cookie,不能阻止已执行的恶意脚本代用户发请求;服务端仍须授权和防护 CSRF。\n * @returns 独立内存 access token 与 Cookie 模式标记;不接触 localStorage。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: { storage: apiClient.tokenStorage.httpOnlyCookie() } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const me = await apiClient.iam.auth.currentUser()\n * if (!me.success) throw new Error(me.error.message)\n * const userId = me.data.id\n * }\n * finally { await apiClient.close() }\n * ```\n */\nexport function createHttpOnlyCookieTokenStorage(): TokenStorage {\n // access token 保存在内存中,用于注入 Authorization 请求头\n let memoryAccessToken: string | null = null\n return {\n async getAccessToken() { return memoryAccessToken },\n async setAccessToken(token) { memoryAccessToken = token },\n // refresh token 由服务端通过 Set-Cookie 设置为 httpOnly cookie;\n // JS 不可见,返回哨兵值让 TokenManager 识别此模式。\n async getRefreshToken() { return HTTPONLY_COOKIE_SENTINEL },\n // refresh token 由服务端管理,JS 端无法写入 httpOnly cookie,忽略此调用。\n async setRefreshToken(_token) { /* 由服务端 Set-Cookie 响应头管理,客户端无需操作 */ },\n async clear() {\n memoryAccessToken = null\n // httpOnly cookie 的清除必须由服务端 logout 端点完成(Set-Cookie: Max-Age=0);\n // 客户端无法通过 JS 直接删除 httpOnly cookie。\n },\n }\n}\n\n// ─── Token 管理器 ─────────────────────────────────────────────────────────────\n\n/** 校验 refresh 响应体中的 Token 字段;httpOnly cookie 模式允许不返回 refreshToken。 */\nconst RefreshTokenPayloadSchema = z.object({\n accessToken: z.string().min(1),\n refreshToken: z.string().min(1).optional(),\n expiresIn: z.number().optional(),\n tokenType: z.string().optional(),\n})\n\n/**\n * 当前代际刷新成功后的同步通知;参数包含敏感凭据,不应记录 Token。\n * 回调在持久写入成功后调用,应自行处理业务异常,不能把抛错当成刷新重试机制。\n *\n * @example 模块内部上下文\n * ```ts\n * const observed: number[] = []\n * const callback: RefreshCallback = tokens => { observed.push(tokens.expiresIn) }\n * callback({ accessToken: 'test-access', expiresIn: 3600, tokenType: 'Bearer' })\n * ```\n */\ntype RefreshCallback = (tokens: TokenPair) => void\n\n/**\n * 创建模块内部 Token 管理器;包用户通过 apiClient.auth 使用,包入口不导出此工厂。\n * 同一代际的并发刷新共享请求;clear/setTokens 同步撤销旧代际,所有存储变更串行提交。\n * 迟到响应不能覆盖新账号、恢复退出凭据或触发旧回调;已开始的存储写入须完成后才能执行后续身份变更。\n * 4xx、无效响应和非 Cookie 模式缺少 refreshToken 会尝试清理凭据;5xx、网络/超时保留凭据并返回 null。\n * 无刷新凭据时通知失败并返回 null;存储读取失败可拒绝 Promise,setTokens/clear 的存储失败向调用方传播。\n * @param storage - 独占的安全存储适配器。\n * @param refreshEndpointUrl - baseUrl 与 refreshPath 组合后的完整 URL。\n * @param fetchFn - 实际 fetch;Cookie 模式发送凭据,普通模式发送 JSON refreshToken。\n * @param onRefreshFailed - 当前代际明确失效后的同步通知。\n * @param refreshTimeoutMs - fetch 中断超时毫秒数;依赖 fetch 实现响应 signal。\n * @returns 内部管理器;不要绕过它直接更新已绑定的 storage。\n *\n * @example 模块内部上下文\n * ```ts\n * const storage = createMemoryTokenStorage()\n * const fetchTokens: typeof fetch = async () => Response.json({ data: { accessToken: 'test-access-next', refreshToken: 'test-refresh-next', expiresIn: 3600 } })\n * const manager = createTokenManager(storage, 'https://api.example.com/api/v1/auth/refresh', fetchTokens)\n * await manager.setTokens({ accessToken: 'test-access', refreshToken: 'test-refresh', expiresIn: 3600, tokenType: 'Bearer' })\n * const off = manager.onTokenRefreshed(tokens => { const lifetime = tokens.expiresIn })\n * try { const refreshed = await manager.refresh() }\n * finally { off(); await manager.clear() }\n * ```\n */\nexport function createTokenManager(\n storage: TokenStorage,\n refreshEndpointUrl: string,\n fetchFn: typeof globalThis.fetch,\n onRefreshFailed?: () => void,\n refreshTimeoutMs: number = 10_000,\n) {\n const callbacks: RefreshCallback[] = []\n let refreshPromise: Promise<TokenPair | null> | null = null\n let refreshController: AbortController | null = null\n let generation = 0\n let sessionInvalid = false\n let storageMutation: Promise<void> = Promise.resolve()\n\n /**\n * 按调用顺序提交异步存储写入;单次失败只拒绝该调用,后续清理/登录仍可运行。\n * @param operation - 本次需要排序的写入操作。\n * @returns 本次操作结果,不吞掉当前写入故障。\n *\n * @example 模块内部上下文\n * ```ts\n * const order: string[] = []\n * await mutateStorage(async () => { order.push('login') })\n * await mutateStorage(async () => { order.push('logout') })\n * ```\n */\n function mutateStorage<T>(operation: () => Promise<T>): Promise<T> {\n const pending = storageMutation.then(operation)\n storageMutation = pending.then(() => {}, () => {})\n return pending\n }\n\n /**\n * 同步推进身份代际并移除旧刷新去重状态;返回旧控制器供调用方排好新写入后中断。\n * @returns 旧刷新控制器或 null;即使底层忽略 abort,代际检查仍拒绝旧结果提交。\n *\n * @example 模块内部上下文\n * ```ts\n * const old = invalidateRefresh()\n * // 生产调用先排入新身份存储变更,再触发可能重入的 abort 监听器。\n * old?.abort()\n * ```\n */\n function invalidateRefresh(): AbortController | null {\n const previousController = refreshController\n generation++\n refreshController = null\n refreshPromise = null\n return previousController\n }\n\n /**\n * 仅当前刷新代际可尝试清理存储并通知失效;旧账号迟到 401 不影响新身份。\n * @param refreshGeneration - 发起刷新时记录的身份代际。\n * @returns 清理完成;存储失败拒绝,由 doRefresh 的异常分支处理。\n *\n * @example 模块内部上下文\n * ```ts\n * await failRefresh(generation)\n * // 传入旧 generation 会在串行写入队列中再次校验并跳过。\n * ```\n */\n async function failRefresh(refreshGeneration: number): Promise<void> {\n await mutateStorage(async () => {\n if (refreshGeneration !== generation)\n return\n await storage.clear()\n if (refreshGeneration === generation) {\n sessionInvalid = true\n onRefreshFailed?.()\n }\n })\n }\n\n /**\n * 同一代际只发起一个刷新请求;旧请求 finally 不能清除新身份的在途状态。\n * @returns 成功且仍有效的 Token,或 null;读取适配器失败可能拒绝。\n *\n * @example 模块内部上下文\n * ```ts\n * const [first, second] = await Promise.all([refresh(), refresh()])\n * // 两个调用共享在途 fetch;结果仍以各自读取到的当前代际为准。\n * ```\n */\n async function refresh(): Promise<TokenPair | null> {\n // 并发去重:同一时刻只发一次 refresh,其它调用复用同一个 Promise\n if (refreshPromise)\n return refreshPromise\n const controller = new AbortController()\n const pending = doRefresh(generation, controller)\n refreshController = controller\n refreshPromise = pending\n try {\n return await pending\n }\n finally {\n // 旧请求结束不能移除换账号后新一轮刷新的去重状态。\n if (refreshPromise === pending) {\n refreshPromise = null\n refreshController = null\n }\n }\n }\n\n /**\n * 等待身份写入完成后读取刷新凭据,执行超时请求并在每个异步边界校验代际。\n * 4xx/无效数据尝试清理;5xx/网络故障保留凭据。只有当前代际成功写入后才通知订阅者。\n * @param refreshGeneration - 请求开始时的代际。\n * @param controller - 本次请求中断控制器;底层 fetch 应响应 signal。\n * @returns 当前刷新 Token 或 null;清理/回调等请求段异常记录为失败,不声称持久清理成功。\n *\n * @example 模块内部上下文\n * ```ts\n * const controller = new AbortController()\n * const tokens = await doRefresh(generation, controller)\n * if (tokens) { const lifetime = tokens.expiresIn }\n * ```\n */\n async function doRefresh(refreshGeneration: number, controller: AbortController): Promise<TokenPair | null> {\n // 登录/退出可能正在异步落盘,刷新只能读取其完成后的凭据。\n await storageMutation\n if (refreshGeneration !== generation)\n return null\n const refreshToken = await storage.getRefreshToken()\n if (refreshGeneration !== generation)\n return null\n if (!refreshToken) {\n sessionInvalid = true\n logger.debug('Token refresh skipped, no refresh token available')\n onRefreshFailed?.()\n return null\n }\n\n logger.debug('Refreshing token')\n // 超时保护:避免 refresh 接口挂死时 dedup Promise 永不 resolve,连带所有等待者 hang。\n const timeoutId = setTimeout(() => controller.abort(), refreshTimeoutMs)\n try {\n // httpOnly cookie 模式:浏览器自动发送 Cookie,请求体无需携带 refreshToken;\n // 同时设置 credentials:'include' 以支持跨域 Cookie 发送。\n const useHttpOnlyCookie = refreshToken === HTTPONLY_COOKIE_SENTINEL\n const requestInit: RequestInit = {\n method: 'POST',\n body: useHttpOnlyCookie ? undefined : JSON.stringify({ refreshToken }),\n credentials: useHttpOnlyCookie ? 'include' : 'same-origin',\n signal: controller.signal,\n }\n if (!useHttpOnlyCookie) {\n requestInit.headers = { 'Content-Type': 'application/json' }\n }\n const response = await fetchFn(refreshEndpointUrl, requestInit)\n if (refreshGeneration !== generation)\n return null\n\n if (!response.ok) {\n // 仅在服务端明确返回 401/4xx 时才清除 Token(refresh token 已失效);\n // 5xx 作为服务端临时故障,保留 Token 让下一次请求重试。\n if (response.status >= 400 && response.status < 500) {\n await failRefresh(refreshGeneration)\n }\n else {\n logger.warn('Token refresh transient failure, keeping tokens', { status: response.status })\n }\n return null\n }\n\n const body = await response.json() as { data?: unknown }\n if (refreshGeneration !== generation)\n return null\n const parsed = RefreshTokenPayloadSchema.safeParse(readTokenPayload(body.data))\n if (!parsed.success) {\n logger.warn('Token refresh returned invalid data', { issues: parsed.error.issues })\n await failRefresh(refreshGeneration)\n return null\n }\n if (!useHttpOnlyCookie && !parsed.data.refreshToken) {\n logger.warn('Token refresh returned invalid data', { issues: [{ path: ['refreshToken'], message: 'Required' }] })\n await failRefresh(refreshGeneration)\n return null\n }\n\n // 默认值兜底,避免不安全强转\n const refreshTokenForStorage = parsed.data.refreshToken ?? HTTPONLY_COOKIE_SENTINEL\n const tokens: TokenPair = {\n accessToken: parsed.data.accessToken,\n refreshToken: refreshTokenForStorage,\n expiresIn: parsed.data.expiresIn ?? 3600,\n tokenType: 'Bearer',\n }\n const committed = await mutateStorage(async () => {\n if (refreshGeneration !== generation)\n return false\n await storage.setAccessToken(tokens.accessToken)\n if (refreshGeneration !== generation)\n return false\n await storage.setRefreshToken(refreshTokenForStorage)\n return refreshGeneration === generation\n })\n if (!committed || refreshGeneration !== generation)\n return null\n sessionInvalid = false\n logger.debug('Token refreshed successfully')\n for (const cb of callbacks) {\n if (refreshGeneration !== generation)\n return null\n cb(tokens)\n }\n return refreshGeneration === generation ? tokens : null\n }\n catch (error) {\n if (refreshGeneration !== generation)\n return null\n // 网络或 fetch 本身抛异常(超时、断网、CORS)视为可重试的暂时性错误,\n // 不清除 Token,仅记录日志。调用方可在下次请求时重试。\n logger.error('Token refresh failed (transient)', { error })\n return null\n }\n finally {\n clearTimeout(timeoutId)\n }\n }\n\n return {\n storage,\n get generation() { return generation },\n get sessionInvalid() { return sessionInvalid },\n refresh,\n async setTokens(tokens: TokenPair) {\n const previousController = invalidateRefresh()\n sessionInvalid = false\n const mutationGeneration = generation\n const pending = mutateStorage(async () => {\n if (mutationGeneration !== generation)\n return\n // 替换完整凭据:新账号未提供 refresh token 时不能继续使用旧账号的值。\n await storage.clear()\n if (mutationGeneration !== generation)\n return\n await storage.setAccessToken(tokens.accessToken)\n if (tokens.refreshToken && mutationGeneration === generation)\n await storage.setRefreshToken(tokens.refreshToken)\n })\n // 先排好新身份写入,再触发 abort 事件;可重入的监听器只能读取新操作后的存储。\n previousController?.abort()\n await pending\n },\n async clear() {\n const previousController = invalidateRefresh()\n const pending = mutateStorage(() => storage.clear())\n previousController?.abort()\n await pending\n },\n onTokenRefreshed(callback: RefreshCallback): () => void {\n callbacks.push(callback)\n return () => {\n const idx = callbacks.indexOf(callback)\n if (idx >= 0)\n callbacks.splice(idx, 1)\n }\n },\n }\n}\n\n/**\n * Token 管理器内部契约,从真实工厂返回值推导;不属于包公开类型。\n *\n * @example 模块内部上下文\n * ```ts\n * const manager: TokenManager = createTokenManager(createMemoryTokenStorage(), 'https://api.example.com/api/v1/auth/refresh', fetch)\n * await manager.clear()\n * // clear 先废弃旧代际,再等待已有写入及本次清理完成。\n * ```\n */\nexport type TokenManager = ReturnType<typeof createTokenManager>\n\n/**\n * 从直接 tokens 或带 tokens 字段的 data 提取待校验内容;本函数不证明字段合法。\n * @param data - 已解析 JSON 包络的 data。\n * @returns 交给 RefreshTokenPayloadSchema 校验的未知值。\n *\n * @example 模块内部上下文\n * ```ts\n * const candidate = readTokenPayload({ tokens: { accessToken: 'test-access', expiresIn: 3600 } })\n * const parsed = RefreshTokenPayloadSchema.safeParse(candidate)\n * if (parsed.success) { const accessToken = parsed.data.accessToken }\n * ```\n */\nfunction readTokenPayload(data: unknown): unknown {\n if (typeof data === 'object' && data !== null && 'tokens' in data) {\n return (data as { tokens: unknown }).tokens\n }\n return data\n}\n","{\n \"apiClient_networkError\": \"Network request failed\",\n \"apiClient_timeout\": \"Request timeout\",\n \"apiClient_serverError\": \"Server error ({status})\",\n \"apiClient_unauthorized\": \"Unauthorized, please log in again\",\n \"apiClient_forbidden\": \"Access forbidden\",\n \"apiClient_notFound\": \"Resource not found\",\n \"apiClient_validationFailed\": \"Request validation failed\",\n \"apiClient_tokenRefreshFailed\": \"Token refresh failed, please log in again\",\n \"apiClient_notInitialized\": \"API client not initialized, please call apiClient.init() first\",\n \"apiClient_alreadyInitialized\": \"API client is already initialized; call apiClient.close() before init()\",\n \"apiClient_configError\": \"API client config error: {error}\",\n \"apiClient_responseValidationFailed\": \"Response validation failed\",\n \"apiClient_unknown\": \"Unknown error\"\n}\n","{\n \"apiClient_networkError\": \"ネットワークリクエストに失敗しました\",\n \"apiClient_timeout\": \"リクエストがタイムアウトしました\",\n \"apiClient_serverError\": \"サーバーエラー({status})\",\n \"apiClient_unauthorized\": \"認証されていません。再度ログインしてください\",\n \"apiClient_forbidden\": \"アクセスが拒否されました\",\n \"apiClient_notFound\": \"リソースが見つかりません\",\n \"apiClient_validationFailed\": \"リクエストの検証に失敗しました\",\n \"apiClient_tokenRefreshFailed\": \"トークンの更新に失敗しました。再度ログインしてください\",\n \"apiClient_notInitialized\": \"APIクライアントが初期化されていません。先にapiClient.init()を呼び出してください\",\n \"apiClient_alreadyInitialized\": \"APIクライアントは初期化済みです。init()の前にapiClient.close()を呼び出してください\",\n \"apiClient_configError\": \"APIクライアントの設定エラー: {error}\",\n \"apiClient_responseValidationFailed\": \"レスポンスの検証に失敗しました\",\n \"apiClient_unknown\": \"不明なエラー\"\n}\n","{\n \"apiClient_networkError\": \"网络请求失败\",\n \"apiClient_timeout\": \"请求超时\",\n \"apiClient_serverError\": \"服务器错误({status})\",\n \"apiClient_unauthorized\": \"未授权,请重新登录\",\n \"apiClient_forbidden\": \"无权限访问\",\n \"apiClient_notFound\": \"请求的资源不存在\",\n \"apiClient_validationFailed\": \"请求参数校验失败\",\n \"apiClient_tokenRefreshFailed\": \"Token刷新失败,请重新登录\",\n \"apiClient_notInitialized\": \"API 客户端未初始化,请先调用 apiClient.init()\",\n \"apiClient_alreadyInitialized\": \"API 客户端已初始化,请先调用 apiClient.close() 再重新初始化\",\n \"apiClient_configError\": \"API 客户端配置错误:{error}\",\n \"apiClient_responseValidationFailed\": \"响应数据校验失败\",\n \"apiClient_unknown\": \"未知错误\"\n}\n","/**\n * @h-ai/api-client — i18n\n *\n * 模块 i18n 文案访问入口。\n * @module api-client-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 * 模块内合法翻译键;由中文词典推导并与现有英文词典配套维护。\n *\n * @example 模块内部上下文\n * ```ts\n * const key: ApiClientMessageKey = 'apiClient_notInitialized'\n * const message = apiClientM(key)\n * ```\n */\ntype ApiClientMessageKey = keyof typeof messagesZhCN\n\n/**\n * 获取 API Client 本地化消息;跟随 Core 当前 locale,不记录凭据或响应正文。\n * @param key - 真实模块消息键。\n * @param options - 语言覆盖及 params 插值选项。\n * @returns 已插值的中文或英文消息。\n *\n * @example 模块内部上下文\n * ```ts\n * const message = apiClientM('apiClient_configError', { locale: 'en-US', params: { error: 'Invalid endpoint' } })\n * ```\n */\nexport const apiClientM = core.i18n.createMessageGetter<ApiClientMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n 'ja-JP': messagesJaJP,\n})\n","/** 认证表单动作:统一选择登录方式及安全 Token 存储。 */\nimport type { apiContract, IamLoginInput, IamRegisterInput } from '@h-ai/api-contract'\nimport type { HaiResult } from '@h-ai/core'\nimport type { ApiClient } from './api-client-types.js'\nimport { core } from '@h-ai/core'\n\n// 仅持有本机 DOM 监听器;业务会话真值仍由服务端撤销与超时决定。\nconst pageExitBindings = new WeakMap<object, Set<() => void>>()\n\n/**\n * 客户端关闭时清理本实例安装的页面监听器,不清理其它实例。\n * @example 模块内部上下文\n * ```ts\n * clearIamPageExitBindings({}) // 没有绑定时幂等。\n * ```\n */\nexport function clearIamPageExitBindings(auth: object): void {\n for (const stop of pageExitBindings.get(auth) ?? [])\n stop()\n pageExitBindings.delete(auth)\n}\n\n/**\n * 认证表单登录输入;默认 password,ldap 使用 identifier/password,email/sms 使用 identifier/code。\n * method/code 为客户端表单辅助字段,按所选方式映射到标准 IAM procedure。\n *\n * @example\n * ```ts\n * import type { IamAuthLoginInput } from '@h-ai/api-client'\n * const input: IamAuthLoginInput = { identifier: 'alice@example.com', password: '', method: 'email', code: '123456' }\n * ```\n */\nexport type IamAuthLoginInput = IamLoginInput & { method?: 'password' | 'ldap' | 'email' | 'sms', code?: string }\n\n/**\n * 将含标准 IAM 契约的客户端绑定为认证表单动作;包用户通过 apiClient.createAuthActions 调用。\n * 登录/注册成功后等待 client.auth.setTokens,失败 HaiResult 原样返回;存储失败拒绝 Promise,不能据服务端成功就显示已登录。\n * 调用方必须先 init 并启用 auth;未启用 auth 时客户端 setTokens 不会建立 Token 管理器。\n * @param client - 具有标准 IAM procedure 与 setTokens 能力的已初始化客户端。\n * @param client.iam - 标准 IAM 契约的领域分组。\n * @param client.iam.auth - 公开配置、登录、注册、验证码和密码重置 procedure。\n * @param client.auth - 认证成功后的凭据写入能力(setTokens)。\n * @returns publicConfig、按方式登录、注册、验证码和密码重置动作。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const actions = apiClient.createAuthActions(apiClient)\n * // 运行时凭据来自已验证的表单;此处只请求公开配置,不提交示例密码。\n * const config = await actions.publicConfig()\n * if (!config.success) throw new Error(config.error.message)\n * }\n * finally { await apiClient.close() }\n * ```\n */\nexport function createIamAuthActions(client: {\n iam: { auth: Pick<ApiClient<{ iam: typeof apiContract.iam }>['iam']['auth'], 'publicConfig' | 'createCaptcha' | 'closeSession' | 'login' | 'loginWithLdap' | 'loginWithOtp' | 'register' | 'sendOtp' | 'requestPasswordReset' | 'confirmPasswordReset' | 'renewPassword' | 'reauthenticate'> }\n auth: Pick<ApiClient<{ iam: typeof apiContract.iam }>['auth'], 'setTokens'>\n}) {\n /**\n * 认证成功后等待凭据保存;失败 HaiResult 不写存储,写入失败向调用方拒绝。\n * @param result - 标准 IAM 登录/注册结果,成功数据含 TokenPair。\n * @returns 同一结果;不能把服务端成功与本地安全存储成功混为一谈。\n *\n * @example 模块内部上下文\n * ```ts\n * const failure = { success: false as const, error: { code: 'test:denied', message: 'Denied', httpStatus: 401 } }\n * const result = await save(failure)\n * if (!result.success) { const message = result.error.message }\n * ```\n */\n async function save<T extends { tokens: Parameters<typeof client.auth.setTokens>[0] }>(result: HaiResult<T>) {\n if (result.success)\n await client.auth.setTokens(result.data.tokens)\n return 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 // 不清理 Cookie/本地存储,避免迟到响应覆盖下一文档刚建立的新身份。\n void client.iam.auth.closeSession({}, { context: { keepalive: true, autoRefresh: false } }).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 const bindings = pageExitBindings.get(client.auth) ?? new Set<() => void>()\n const stop = () => {\n target.removeEventListener('pagehide', onPageHide)\n bindings.delete(stop)\n if (!bindings.size)\n pageExitBindings.delete(client.auth)\n }\n bindings.add(stop)\n pageExitBindings.set(client.auth, bindings)\n return stop\n },\n publicConfig: () => client.iam.auth.publicConfig(),\n createCaptcha: (input: Parameters<typeof client.iam.auth.createCaptcha>[0]) => client.iam.auth.createCaptcha(input),\n login: async (input: IamAuthLoginInput) => {\n const result = input.method === 'ldap'\n ? await client.iam.auth.loginWithLdap({ username: input.identifier, password: input.password, captcha: input.captcha, expectedUserId: input.expectedUserId })\n : input.method === 'email' || input.method === 'sms'\n ? await client.iam.auth.loginWithOtp({ identifier: input.identifier, code: input.code ?? '', captcha: input.captcha, expectedUserId: input.expectedUserId })\n : await client.iam.auth.login({ identifier: input.identifier, password: input.password, captcha: input.captcha, expectedUserId: input.expectedUserId })\n return save(result)\n },\n register: async (input: IamRegisterInput) => save(await client.iam.auth.register(input)),\n sendOtp: (identifier: string, options?: { captcha?: string }) => client.iam.auth.sendOtp({ identifier, ...options }),\n requestPasswordReset: (identifier: string, options?: { captcha?: string }) => client.iam.auth.requestPasswordReset({ identifier, ...options }),\n confirmPasswordReset: (token: string, newPassword: string) => client.iam.auth.confirmPasswordReset({ token, newPassword }),\n renewPassword: (input: { identifier: string, password: string, newPassword: string, captcha?: string }) => client.iam.auth.renewPassword(input),\n reauthenticate: (password: string, operation: string) => client.iam.auth.reauthenticate({ password, operation }),\n }\n}\n","/**\n * @h-ai/api-client — 类型与错误码\n *\n * 公共类型(TokenPair、TokenStorage、ApiClientConfig 等)与标准错误集合(HaiApiClientError)。\n * 不包含实现,仅用于跨文件共享与对外导出。\n * @module api-client-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport type { CryptoFunctions } from '@h-ai/crypto'\nimport type { AnyContractRouter, ContractRouterClient } from '@orpc/contract'\nimport type { JsonifiedClient } from '@orpc/openapi-client'\nimport { core } from '@h-ai/core'\n\n// ─── 错误码 ───────────────────────────────────────────────────────────────────\n\nconst ApiClientErrorInfo = {\n NETWORK_ERROR: '001:502',\n TIMEOUT: '002:504',\n SERVER_ERROR: '003:502',\n UNAUTHORIZED: '004:401',\n FORBIDDEN: '005:403',\n NOT_FOUND: '006:404',\n VALIDATION_FAILED: '007:400',\n TOKEN_REFRESH_FAILED: '008:401',\n PAYLOAD_TOO_LARGE: '020:413',\n NOT_INITIALIZED: '010:500',\n CONFIG_ERROR: '011:500',\n UNKNOWN: '099:500',\n} as const satisfies ErrorInfo\n\n/**\n * 客户端标准错误集合;包含稳定 code/httpStatus 与构造错误的方法,业务调用先检查 success。\n * 传输失败在 procedure 代理中映射;服务端 HaiResult 的业务错误保持原契约。\n *\n * @example\n * ```ts\n * import { apiClient, HaiApiClientError } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1' })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await apiClient.iam.auth.currentUser()\n * if (!result.success && result.error.code === HaiApiClientError.UNAUTHORIZED.code) {\n * // 由应用切换到登录页面;不输出 Token。\n * const message = result.error.message\n * }\n * }\n * finally { await apiClient.close() }\n * ```\n */\nexport const HaiApiClientError = core.error.buildHaiErrorsDef('api-client', ApiClientErrorInfo)\n\n// ─── Token ────────────────────────────────────────────────────────────────────\n\n/**\n * 短期 access token、可选 refresh token 与有效期;HTTP 头使用 Bearer。\n * Cookie 模式的 refresh token 由服务端管理,不能要求浏览器读取或持久化它。\n *\n * @example\n * ```ts\n * import type { TokenPair } from '@h-ai/api-client'\n * const tokens: TokenPair = { accessToken: 'test-access', expiresIn: 3600, tokenType: 'Bearer' }\n * // 示例为测试凭据;生产 Token 只能来自可信服务端认证响应。\n * ```\n */\nexport interface TokenPair {\n /** 短期访问凭证,请求时作为 `Authorization: Bearer ...`。 */\n readonly accessToken: string\n /** 长期刷新凭证;httpOnly cookie 模式下由服务端管理,响应体中可能不存在。 */\n readonly refreshToken?: string\n /** accessToken 有效期秒数。 */\n readonly expiresIn: number\n /** 固定为 `'Bearer'`。 */\n readonly tokenType: 'Bearer'\n}\n\n/**\n * 异步安全凭据存储契约;null 仅表示无可用凭据,读写故障须拒绝 Promise。\n * 浏览器使用内存 access token/httpOnly refresh Cookie,原生端注入平台安全存储,禁止敏感 Token 放入 localStorage。\n * 已绑定客户端的身份切换通过 client.auth,避免绕过刷新代际与串行写入;部分写入失败须执行清理并报告清理失败。\n *\n * 内置实现:\n * - `apiClient.tokenStorage.httpOnlyCookie()` — **默认**;浏览器端推荐,refresh token 由服务端管理,防 XSS。\n * - `apiClient.tokenStorage.memory()` — Node.js 测试 / SSR(需显式传入)。\n *\n * Capacitor / 小程序场景请由对应模块提供(例如 `@h-ai/capacitor`)。\n *\n * @example\n * ```ts\n * import { apiClient, type TokenStorage } from '@h-ai/api-client'\n * const storage: TokenStorage = apiClient.tokenStorage.memory()\n * await storage.setAccessToken('test-access')\n * const accessToken = await storage.getAccessToken()\n * await storage.clear()\n * ```\n */\nexport interface TokenStorage {\n /**\n * 读取本适配器可用 access token;无凭据返回 null,存储故障拒绝。\n * @returns Token 或 null;不能把读取失败当成不存在。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const storage = apiClient.tokenStorage.memory()\n * const accessToken = await storage.getAccessToken()\n * ```\n */\n readonly getAccessToken: () => Promise<string | null>\n /**\n * 保存 access token;已绑定客户端应通过 auth.setTokens 维护代际。\n * @param token - 可信服务端认证返回的 access token。\n * @returns 写入完成;底层故障须拒绝。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const storage = apiClient.tokenStorage.memory()\n * await storage.setAccessToken('test-access')\n * await storage.clear()\n * ```\n */\n readonly setAccessToken: (token: string) => Promise<void>\n /**\n * 读取 refresh token 或 Cookie 模式内部哨兵;null 仅表示没有凭据。\n * @returns Token、模式哨兵或 null,不是对 httpOnly Cookie 内容的脚本读取。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const storage = apiClient.tokenStorage.memory()\n * const refreshToken = await storage.getRefreshToken()\n * // 内存适配器初始为空;Cookie 适配器由管理器识别模式哨兵。\n * ```\n */\n readonly getRefreshToken: () => Promise<string | null>\n /**\n * 保存 refresh token;Cookie 实现不通过脚本修改服务端 Cookie。\n * @param token - 服务端返回的刷新凭据;禁止写入 Web Storage。\n * @returns 写入完成;原生安全存储故障须拒绝。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const storage = apiClient.tokenStorage.memory()\n * await storage.setRefreshToken('test-refresh')\n * await storage.clear()\n * ```\n */\n readonly setRefreshToken: (token: string) => Promise<void>\n /**\n * 清空适配器可管理的凭据;Cookie 实现只能清内存 access token。\n * @returns 清理完成;失败须拒绝,服务端 Cookie/会话另由退出接口撤销。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const storage = apiClient.tokenStorage.memory()\n * await storage.clear()\n * const remaining = await storage.getAccessToken()\n * ```\n */\n readonly clear: () => Promise<void>\n}\n\n// ─── 配置 ─────────────────────────────────────────────────────────────────────\n\n/**\n * 启用 Token 注入与自动刷新;默认 Cookie 适配器,refreshPath 相对 baseUrl。\n * 网络/超时/5xx 为暂时失败,保留凭据;明确失效才清理并通知。回调不应抛出或记录 Token。\n *\n * @example\n * ```ts\n * import { apiClient, type AuthConfig } from '@h-ai/api-client'\n * const auth: AuthConfig = { storage: apiClient.tokenStorage.memory(), refreshPath: '/auth/refresh', refreshTimeoutMs: 5000, onTokenRefreshed: tokens => { const lifetime = tokens.expiresIn } }\n * // Node/SSR 选择独立内存存储;浏览器通常直接配置 auth: {}。\n * ```\n */\nexport interface AuthConfig {\n /** refresh明确失效后原地恢复同一用户会话,成功时只重试原请求一次;取消/结果不明不得返回true。 */\n readonly recoverSession?: () => Promise<boolean>\n /** 登录会话终端分组;默认web,可选app/desktop/api。服务端必须允许该类;不代表物理设备证明。 */\n readonly terminalType?: 'web' | 'app' | 'desktop' | 'api'\n /**\n * Token 存储适配器;默认 `apiClient.tokenStorage.httpOnlyCookie()`(refresh token 由服务端管理,防 XSS)。\n *\n * - SSR / Node.js 测试场景请显式传入 `apiClient.tokenStorage.memory()`。\n * - 浏览器刷新后通过 httpOnly Cookie 恢复会话,不将 Token 持久化到 Web Storage。\n * - Capacitor / 小程序请使用对应平台的安全存储适配器(如 `@h-ai/capacitor`)。\n */\n readonly storage?: TokenStorage\n /** 刷新接口路径,相对于 `baseUrl`,默认 `/auth/refresh`。 */\n readonly refreshPath?: string\n /**\n * 刷新请求超时(ms),默认 10_000。\n * 避免 refresh 接口上游挂死时所有 dedup 调用方一起 hang。\n * 超时后按现有 transient 失败处理(不清 token,下次请求可重试)。\n */\n readonly refreshTimeoutMs?: number\n /** 刷新成功回调,用于同步状态(例如多 Tab 广播)。 */\n readonly onTokenRefreshed?: (tokens: TokenPair) => void\n /** 刷新失败回调(如 refresh token 已失效),通常用于跳转登录页。 */\n readonly onRefreshFailed?: () => void\n}\n\n/**\n * 客户端初始化配置;baseUrl 包含 API 前缀,headers/fetch 用于平台适配。\n * auth 缺省时不创建 TokenManager;timeout 是底层请求中断期限,不代表服务端已取消副作用。\n * transport 需要已初始化 crypto,首次业务请求前协商会话;客户端不接收服务端私钥。\n *\n * @example\n * ```ts\n * import { apiClient, type ApiClientConfig } from '@h-ai/api-client'\n * const config: ApiClientConfig = { baseUrl: 'https://api.example.com/api/v1', auth: { storage: apiClient.tokenStorage.memory() }, timeout: 15000, clientName: 'orders-web', headers: () => ({ 'x-app-channel': 'web' }) }\n * const initialized = await apiClient.init(config)\n * if (!initialized.success) throw new Error(initialized.error.message)\n * await apiClient.close()\n * ```\n */\nexport interface ApiClientConfig {\n /** API service 完整 baseUrl,通常含 `apiPrefix`,例如 `https://api.example.com/api/v1`。 */\n readonly baseUrl: string\n /** 认证配置;不传则不启用 Token 注入与自动刷新。 */\n readonly auth?: AuthConfig\n /** 请求超时(ms),默认 30000。 */\n readonly timeout?: number\n /** 客户端标识,写入 `x-request-client` 请求头,默认 `hai-api-client`。 */\n readonly clientName?: string\n /** 额外请求头,可为静态对象或异步函数。 */\n readonly headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>)\n /** 自定义 fetch(测试 mock / Capacitor / 小程序桥接)。 */\n readonly fetch?: typeof globalThis.fetch\n /**\n * 启用端到端传输加密(opt-in)。\n *\n * 配置后所有出站请求会在底层 fetch 之上做一次混合加密,并自动解密\n * 标记为 `X-Encrypted: true` 的响应。服务端需启用对应的 `serv.createApp({ transport })`。\n *\n * `crypto` 字段结构与 `@h-ai/crypto` 的 `crypto` 实例兼容;通常直接传入:\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * import { crypto } from '@h-ai/crypto'\n *\n * const cryptography = await crypto.init()\n * if (!cryptography.success) throw new Error(cryptography.error.message)\n * try {\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', transport: { crypto } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * await apiClient.close()\n * }\n * finally { await crypto.close() }\n * ```\n */\n readonly transport?: ApiClientTransportConfig\n}\n\n// ─── 传输加密 ────────────────────────────────────────────────────────────────\n\n/**\n * 可选加密会话配置;密钥协商路径相对 baseUrl,并与服务端配置匹配。\n * crypto 必须先初始化;服务器版本公钥通过受保护的 TLS 握手取得,不配置服务端私钥。\n * 传输防重放不能代替业务幂等性,失败后不要自动重放非幂等操作。\n *\n * @example\n * ```ts\n * import { apiClient, type ApiClientTransportConfig } from '@h-ai/api-client'\n * import { crypto } from '@h-ai/crypto'\n * const cryptography = await crypto.init()\n * if (!cryptography.success) throw new Error(cryptography.error.message)\n * try {\n * const transport: ApiClientTransportConfig = { crypto, keyExchangePath: '/_hai/key-exchange' }\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', transport })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * await apiClient.close()\n * }\n * finally { await crypto.close() }\n * ```\n */\nexport interface ApiClientTransportConfig {\n /** 可选本地明文 UTF-8 字节上限;缺省自动跟随服务端,指定时取较小值。 */\n readonly maxPlaintextBytes?: number\n /**\n * crypto 服务实例(通常直接传入 `@h-ai/crypto` 的 `crypto`)。\n * api-client 通过 `crypto.transport.createClient(...)` 创建会话。\n */\n readonly crypto: CryptoFunctions\n /**\n * 密钥协商端点路径,相对于 `baseUrl`,默认 `/_hai/key-exchange`。\n *\n * 必须与服务端 `serv.createApp({ transport: { keyExchangePath } })` 一致。\n */\n readonly keyExchangePath?: string\n}\n\n// ─── Client 接口 ──────────────────────────────────────────────────────────────\n\n/**\n * 当前客户端的凭据辅助操作;仅 init 启用 auth 时建立实际管理器。\n * setTokens/clear 撤销旧刷新代际并等待串行存储变更;故障拒绝 Promise,调用方处理失败。\n * clear 不能撤销服务端会话或删除 httpOnly Cookie,退出仍需服务端接口成功。\n *\n * @example\n * ```ts\n * import { apiClient, type ApiClientAuth } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const auth: ApiClientAuth = apiClient.auth\n * const off = auth.onTokenRefreshed(tokens => { const lifetime = tokens.expiresIn })\n * off()\n * await auth.clear()\n * }\n * finally { await apiClient.close() }\n * ```\n */\nexport interface ApiClientAuth {\n /**\n * 替换整对凭据并同步废弃旧刷新代际;缺省 refreshToken 时清除旧值。\n * @param tokens - 服务端成功认证返回的真实 TokenPair。\n * @returns 写入完成;安全存储失败拒绝 Promise,调用方应清理部分凭据并报告清理失败。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const form = { identifier: 'alice', password: 'test-password' }\n * const login = await apiClient.iam.auth.login(form)\n * if (!login.success) throw new Error(login.error.message)\n * try { await apiClient.auth.setTokens(login.data.tokens) }\n * catch (writeError) {\n * try { await apiClient.auth.clear() }\n * catch (cleanupError) { throw new AggregateError([writeError, cleanupError], 'Credential persistence and cleanup failed') }\n * throw writeError\n * }\n * }\n * finally { await apiClient.close() }\n * ```\n */\n readonly setTokens: (tokens: TokenPair) => Promise<void>\n /**\n * 同步废弃旧刷新代际并等待存储清理;失败拒绝 Promise,不声称已持久删除。\n * 服务端会话与 httpOnly Cookie 仍需服务端退出撤销;本操作不能替代退出接口。\n * @returns 本地清理完成,不返回 HaiResult。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const logout = await apiClient.iam.auth.logout({})\n * await apiClient.auth.clear()\n * if (!logout.success) throw new Error(logout.error.message)\n * // 只有服务端退出和本地清理都成功,才能确认两侧均已处理。\n * }\n * finally { await apiClient.close() }\n * ```\n */\n readonly clear: () => Promise<void>\n /**\n * 订阅当前管理器刷新成功事件;回调在写入后同步调用,避免抛错或输出 Token。\n * @param callback - 接收 TokenPair 的同步回调。\n * @returns 取消订阅函数;未启用 auth 时返回空操作。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const off = apiClient.auth.onTokenRefreshed(tokens => { const expiresIn = tokens.expiresIn })\n * off()\n * }\n * finally { await apiClient.close() }\n * ```\n */\n readonly onTokenRefreshed: (callback: (tokens: TokenPair) => void) => () => void\n}\n\n/**\n * 客户端生命周期与认证辅助能力;init 的 HaiResult 必须先检查 success。\n * 同一实例并发 init 共享在途初始化,已初始化后再 init 返回 CONFIG_ERROR;切换配置先 close。\n * config 为脱敏快照;close 清理运行状态/加密会话,不等于服务端退出或凭据存储清理。\n *\n * @example\n * ```ts\n * import { apiClient, type ApiClientLifecycle } from '@h-ai/api-client'\n * const client: ApiClientLifecycle = apiClient\n * const initialized = await client.init({ baseUrl: 'https://api.example.com/api/v1' })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * const active = client.isInitialized\n * const snapshot = client.config\n * await client.close()\n * ```\n */\nexport interface ApiClientLifecycle {\n /**\n * 观察一次逻辑调用的最终结果;刷新重试保留关联 ID,不包含请求正文或凭据。\n * close 清理订阅;回调异常不影响请求,返回函数可提前取消。\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const unsubscribe = apiClient.observeRequests(record => { const requestId = record.requestId })\n * unsubscribe()\n * ```\n */\n readonly observeRequests: (observer: ApiRequestObserver) => () => void\n /**\n * 构造并提交客户端运行状态;初始化失败返回 CONFIG_ERROR,未提交半初始化对象。\n * 并发 init 共享同一初始化;已初始化再调用会失败,切换配置先 close。\n * @param config - baseUrl、认证、fetch 与可选加密配置。\n * @returns HaiResult;必须先判断 success,再发起 procedure。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await apiClient.iam.auth.publicConfig()\n * if (!result.success) throw new Error(result.error.message)\n * }\n * finally { await apiClient.close() }\n * ```\n */\n readonly init: (config: ApiClientConfig) => Promise<HaiResult<void>>\n /**\n * 销毁当前加密会话并清除运行状态,允许重新 init;不发送服务端退出、不清空外置存储。\n * @returns 资源清理完成;不是 HaiResult,不应读取 success。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * await apiClient.close()\n * const reinitialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!reinitialized.success) throw new Error(reinitialized.error.message)\n * await apiClient.close()\n * ```\n */\n readonly close: () => Promise<void>\n /** 当前脱敏后的运行配置快照;未初始化时为 `null`。 */\n readonly config: ApiClientConfig | null\n /** 是否已完成初始化。 */\n readonly isInitialized: boolean\n /** Token 辅助操作。 */\n readonly auth: ApiClientAuth\n}\n\n/**\n * 逻辑请求诊断摘要。HTTP 200 的失败 HaiResult 同样 success=false。\n * @example\n * ```ts\n * import type { ApiRequestRecord } from '@h-ai/api-client'\n * const record: ApiRequestRecord = { requestId: 'request-1', procedure: 'orders.get', timestamp: 1, duration: 10, success: false, attempts: 1, errorCode: 'orders:missing' }\n * ```\n */\nexport interface ApiRequestRecord {\n readonly requestId: string\n readonly procedure: string\n readonly timestamp: number\n readonly duration: number\n readonly success: boolean\n readonly attempts: number\n readonly errorCode?: string\n readonly method?: string\n readonly url?: string\n readonly status?: number\n}\n\n/**\n * 同步观察者,只做有界收集。\n * @example\n * ```ts\n * import { apiClient, type ApiRequestObserver } from '@h-ai/api-client'\n * const observer: ApiRequestObserver = record => { const failed = !record.success }\n * const unsubscribe = apiClient.observeRequests(observer)\n * unsubscribe()\n * ```\n */\nexport type ApiRequestObserver = (record: ApiRequestRecord) => void\n\n/**\n * 由 oRPC 契约推导的 JSON typed client,与生命周期组合;procedure 的输入/输出由契约决定。\n * 调用先检查初始化和业务 HaiResult;自定义契约不能覆盖 init/close/config/isInitialized/auth 顶层成员。\n *\n * @example\n * ```ts\n * import { apiClient, type ApiClient } from '@h-ai/api-client'\n * import { apiContract } from '@h-ai/api-contract'\n * const contract = apiContract.create({ iam: apiContract.iam })\n * const client: ApiClient<typeof contract> = apiClient.create(contract)\n * const initialized = await client.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await client.iam.auth.currentUser()\n * if (!result.success) throw new Error(result.error.message)\n * const userId = result.data.id\n * }\n * finally { await client.close() }\n * ```\n */\nexport type ApiClient<TContract extends AnyContractRouter>\n = ApiClientLifecycle & JsonifiedClient<ContractRouterClient<TContract>>\n","/**\n * @h-ai/api-client — 主实现\n *\n * 包含两部分:\n * - `createApiClient(contract)`:根据 oRPC contract 创建 typed client 工厂(内部实现)。\n * - `apiClient`:统一公开入口;自身就是默认单例,同时挂载 `create` / `tokenStorage`。\n *\n * Client 同时具备生命周期能力(init/close/config/isInitialized/auth)和 contract\n * 推导出的 procedure 嵌套调用能力(如 `client.iam.auth.login(input)`)。\n *\n * 内置能力:\n * - 401 自动 refresh + 单次重试\n * - 并发 refresh 共享 Promise\n * - 请求超时(AbortController)\n * - `ORPCError` → `HaiApiClientError` 标准化映射\n * - 未初始化时调用 procedure 返回 `NOT_INITIALIZED`,不抛异常\n *\n * @example 默认单例\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n *\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const me = await apiClient.iam.auth.currentUser()\n * if (!me.success) throw new Error(me.error.message)\n * const userId = me.data.id\n * }\n * finally { await apiClient.close() }\n * ```\n *\n * @example 自定义 contract\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * import { apiContract } from '@h-ai/api-contract'\n *\n * const client = apiClient.create(apiContract.create({ iam: apiContract.iam }))\n * const initialized = await client.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await client.iam.auth.publicConfig()\n * if (!result.success) throw new Error(result.error.message)\n * }\n * finally { await client.close() }\n * ```\n *\n * @module api-client-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { TransportClient } from '@h-ai/crypto'\nimport type { AnyContractRouter, ContractRouterClient } from '@orpc/contract'\nimport type { JsonifiedClient } from '@orpc/openapi-client'\nimport type { TokenManager } from './api-client-auth.js'\nimport type {\n ApiClient,\n ApiClientAuth,\n ApiClientConfig,\n ApiClientLifecycle,\n ApiRequestObserver,\n ApiRequestRecord,\n} from './api-client-types.js'\nimport { apiContract, HaiErrorSchema, IamSessionErrorCodeSchema } from '@h-ai/api-contract'\nimport { core, err, ok } from '@h-ai/core'\nimport { TransportBodyTooLargeError, TransportTimeError } from '@h-ai/crypto'\nimport { createORPCClient, ORPCError } from '@orpc/client'\nimport { OpenAPILink } from '@orpc/openapi-client/fetch'\nimport {\n createHttpOnlyCookieTokenStorage,\n createMemoryTokenStorage,\n createTokenManager,\n} from './api-client-auth.js'\nimport { apiClientM } from './api-client-i18n.js'\nimport { clearIamPageExitBindings, createIamAuthActions } from './api-client-iam.js'\nimport { HaiApiClientError } from './api-client-types.js'\n\nconst DEFAULT_TIMEOUT = 30_000\nconst DEFAULT_CLIENT_NAME = 'hai-api-client'\nconst DEFAULT_REFRESH_PATH = apiContract.pathOf(apiContract.iam.auth.refresh)\nconst AUTH_ENTRY_PATHS = [\n apiContract.pathOf(apiContract.iam.auth.login),\n apiContract.pathOf(apiContract.iam.auth.loginWithOtp),\n apiContract.pathOf(apiContract.iam.auth.loginWithLdap),\n apiContract.pathOf(apiContract.iam.auth.requestPasswordReset),\n apiContract.pathOf(apiContract.iam.auth.confirmPasswordReset),\n apiContract.pathOf(apiContract.iam.auth.register),\n]\nconst TRAILING_SLASHES_REGEX = /\\/+$/\nconst REQUEST_OBSERVATION = Symbol('hai.api-client.request-observation')\n/**\n * 内部构造的可变摘要,不对消费者暴露写入能力。\n * @example 模块内部上下文\n * ```ts\n * function update(record: MutableRequestRecord) { record.attempts += 1 }\n * ```\n */\ntype MutableRequestRecord = { -readonly [Key in keyof ApiRequestRecord]: ApiRequestRecord[Key] }\n\n/** Token 存储方案集合:内存 / httpOnly Cookie。 */\nconst tokenStorage = {\n /** 内存存储——SSR / Node 测试或一次性会话使用。 */\n memory: createMemoryTokenStorage,\n /** httpOnly Cookie 存储——浏览器场景推荐方案。 */\n httpOnlyCookie: createHttpOnlyCookieTokenStorage,\n} as const\n\n// ─── 工厂 ─────────────────────────────────────────────────────────────────────\n\n/**\n * 按真实 oRPC 契约创建独立 typed client;公开调用入口为 apiClient.create(contract)。\n * init 返回 HaiResult,成功后才调用 procedure;已初始化实例必须先 close 再换配置。\n * procedure 将传输异常转换为 HaiResult;auth.setTokens/clear 的安全存储失败仍会拒绝 Promise。\n * SSR 应为每个请求创建客户端和内存存储;不共享默认单例中的用户凭据。\n * @param contract - 已定义的嵌套 oRPC contract;顶层命名不能与生命周期成员冲突。\n * @returns 生命周期、认证辅助操作与契约推导出的 procedure 代理。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * import { apiContract } from '@h-ai/api-contract'\n * const client = apiClient.create(apiContract.create({ iam: apiContract.iam }))\n * const initialized = await client.init({ baseUrl: 'https://api.example.com/api/v1', auth: { storage: apiClient.tokenStorage.memory() }, timeout: 10000 })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await client.iam.auth.currentUser()\n * if (!result.success) throw new Error(result.error.message)\n * const userId = result.data.id\n * }\n * finally { await client.close() }\n * ```\n */\nexport function createApiClient<const TContract extends AnyContractRouter>(\n contract: TContract,\n): ApiClient<TContract> {\n // 客户端运行时状态\n const state: {\n config: ApiClientConfig | null\n rawClient: JsonifiedClient<ContractRouterClient<TContract>> | null\n tokenManager: TokenManager | undefined\n transport: TransportClient | undefined\n observers: Set<ApiRequestObserver>\n } = {\n config: null,\n rawClient: null,\n tokenManager: undefined,\n transport: undefined,\n observers: new Set(),\n }\n\n const auth: ApiClientAuth = {\n async setTokens(tokens) { await state.tokenManager?.setTokens(tokens) },\n async clear() { await state.tokenManager?.clear() },\n onTokenRefreshed(callback) {\n return state.tokenManager?.onTokenRefreshed(callback) ?? (() => {})\n },\n }\n\n let initPromise: Promise<HaiResult<void>> | null = null\n const lifecycle: ApiClientLifecycle = {\n async init(config) {\n // 并发重入防护:同时多次 init() 只执行一次。\n // 另一个调用方 await 同一个 in-flight Promise,避免重复创建 raw client / token manager。\n if (initPromise)\n return initPromise\n // 已初始化后必须先 close() 再重新 init,避免旧 transport 会话被新状态覆盖后无法 destroy。\n if (state.rawClient) {\n return err(\n HaiApiClientError.CONFIG_ERROR,\n apiClientM('apiClient_alreadyInitialized'),\n )\n }\n initPromise = (async (): Promise<HaiResult<void>> => {\n let nextTransport: TransportClient | undefined\n try {\n nextTransport = buildTransportSession(config)\n const nextTokenManager = buildTokenManager(config, nextTransport)\n const nextRawClient = buildRawClient(contract, config, nextTokenManager, nextTransport)\n\n // 所有可能失败的构建步骤完成后再提交状态,避免调用方看到半初始化 client。\n state.config = config\n state.transport = nextTransport\n state.tokenManager = nextTokenManager\n state.rawClient = nextRawClient\n return ok(undefined)\n }\n catch (error) {\n // 构建过程中如果 transport 已创建但后续失败,必须主动释放,避免会话/定时器泄漏。\n nextTransport?.destroy()\n state.config = null\n state.rawClient = null\n state.tokenManager = undefined\n state.transport = undefined\n return err(\n HaiApiClientError.CONFIG_ERROR,\n apiClientM('apiClient_configError', { params: { error: String(error) } }),\n error,\n )\n }\n })()\n try {\n return await initPromise\n }\n finally {\n initPromise = null\n }\n },\n async close() {\n clearIamPageExitBindings(auth)\n state.observers.clear()\n state.transport?.destroy()\n state.config = null\n state.rawClient = null\n state.tokenManager = undefined\n state.transport = undefined\n },\n get config() { return state.config ? core.sanitize.sanitizeSensitiveFields(state.config) : null },\n get isInitialized() { return state.rawClient !== null },\n auth,\n observeRequests(observer) {\n state.observers.add(observer)\n return () => {\n state.observers.delete(observer)\n }\n },\n }\n\n // 生命周期成员优先,其余属性只记录真实契约路径,末端调用才交给 rawClient。\n // 契约顶层不能与 init/close/config/isInitialized/auth 重名;此处不提供额外路由注册。\n return new Proxy({}, {\n get(_target, property) {\n // Proxy 的属性键不一定是 string,也可能是 Symbol(例如某些运行时内部探测)。\n // 本 client 只处理字符串路径;非字符串键直接忽略,避免把 Symbol 误当成 API 路径的一部分。\n if (typeof property !== 'string')\n return undefined\n\n // 顶层字段命中 lifecycle 时,直接返回真实成员。\n // 例如:\n // - property === 'init' → 返回 init 函数\n // - property === 'auth' → 返回 auth 对象\n // 这样 `client.init()` / `client.auth.clear()` 就能像普通对象一样工作。\n if (property in lifecycle) {\n return Reflect.get(lifecycle, property)\n }\n\n // 走到这里,说明它不是生命周期字段,那就把它当成 contract 路径的第一段。\n // 例如访问 `client.iam` 时,这里会创建一个 path = ['iam'] 的嵌套 Proxy。\n // 后续如果继续访问 `.auth.login`,path 会逐步变成:\n // ['iam', 'auth'] → ['iam', 'auth', 'login']\n // 最终在函数调用时再映射到真正的 rawClient.iam.auth.login(...args)。\n return createProcedureProxy(state, [property])\n },\n // 这里的返回值在运行时是一个 Proxy,但从“可用能力”上看,它符合 `ApiClient<TContract>`:\n // - 一部分成员来自 lifecycle\n // - 另一部分成员来自 contract 推导出的 procedure 层级\n // 因此使用类型断言把这个动态对象声明为最终对外的 typed client。\n }) as ApiClient<TContract>\n}\n\n// ─── 默认单例 ─────────────────────────────────────────────────────────────────\n\n/**\n * 默认 API typed client:绑定 `iam` / `storage` / `ai` 三个领域。\n *\n * 大多数应用只需 `import { apiClient }` 并调用 `apiClient.init()` 即可。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n *\n * const initialized = await apiClient.init({\n * baseUrl: 'http://localhost:3000/api/v1',\n * auth: { refreshPath: '/auth/refresh' },\n * })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await apiClient.iam.auth.currentUser()\n * if (!result.success) throw new Error(result.error.message)\n * }\n * finally { await apiClient.close() }\n * ```\n */\nconst defaultApiContract = apiContract.create({ iam: apiContract.iam, storage: apiContract.storage, ai: apiContract.ai })\n\n/**\n * 默认 iam/storage/ai typed client 与 create、tokenStorage、createAuthActions 命名空间的交叉类型。\n * 具体项目契约使用 apiClient.create 的返回类型,不给默认单例附加未知 procedure。\n *\n * @example\n * ```ts\n * import { apiClient, type DefaultApiClient } from '@h-ai/api-client'\n * const client: DefaultApiClient = apiClient\n * const initialized = await client.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try { const actions = client.createAuthActions(client); const result = await actions.publicConfig(); if (!result.success) throw new Error(result.error.message) }\n * finally { await client.close() }\n * ```\n */\nexport type DefaultApiClient = ApiClient<typeof defaultApiContract> & {\n readonly create: typeof createApiClient\n readonly tokenStorage: typeof tokenStorage\n readonly createAuthActions: typeof createIamAuthActions\n}\n\n/**\n * API Client 公开入口:默认 iam/storage/ai 单例,以及独立实例、安全存储和认证动作工厂。\n * 未启用 auth 时不注入或刷新 Token;浏览器 auth: {} 使用 httpOnly Cookie 方案。\n * close 释放客户端运行状态/加密会话,不能替代服务端退出或 auth.clear。\n *\n * @example\n * ```ts\n * import { apiClient } from '@h-ai/api-client'\n * const initialized = await apiClient.init({ baseUrl: 'https://api.example.com/api/v1', auth: {} })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await apiClient.iam.auth.currentUser()\n * if (!result.success) throw new Error(result.error.message)\n * const permissions = result.data.permissions\n * }\n * finally { await apiClient.close() }\n * ```\n */\nexport const apiClient = new Proxy(createApiClient(defaultApiContract) as ApiClient<typeof defaultApiContract>, {\n get(target, property, receiver) {\n if (property === 'create')\n return createApiClient\n if (property === 'tokenStorage')\n return tokenStorage\n if (property === 'createAuthActions')\n return createIamAuthActions\n return Reflect.get(target as object, property, receiver)\n },\n}) as DefaultApiClient\n\n// ─── 内部:procedure 代理 ─────────────────────────────────────────────────────\n\n/**\n * 构造只记录属性路径的内部代理,末端调用交给 callProcedure;不在访问属性时发送请求。\n * @param state - 当前 rawClient,可在 close 后变为 null。\n * @param state.rawClient - 当前 oRPC 客户端;未初始化或关闭后为 null。\n * @param state.observers - 本实例逻辑请求订阅集合。\n * @param path - 已访问的 contract 属性路径。\n * @returns 动态代理;由契约公开类型约束调用路径,不对外承诺任意字符串 API。\n *\n * @example 模块内部上下文\n * ```ts\n * const proxy = createProcedureProxy({ rawClient: null }, ['iam'])\n * // 直到 apply 才调用 callProcedure;未初始化返回失败 HaiResult。\n * ```\n */\nfunction createProcedureProxy<TContract extends AnyContractRouter>(\n state: { rawClient: JsonifiedClient<ContractRouterClient<TContract>> | null, observers?: Set<ApiRequestObserver> },\n path: string[],\n): unknown {\n return new Proxy(() => undefined, {\n get(_t, property) {\n if (typeof property !== 'string')\n return undefined\n return createProcedureProxy(state, [...path, property])\n },\n apply(_t, _thisArg, args) {\n return callObservedProcedure(state.rawClient, path, args, state.observers)\n },\n })\n}\n\n/** 不复制业务入参;通过 oRPC 每次调用的 context 将同一关联传给传输层。 */\nasync function callObservedProcedure(rawClient: unknown, path: string[], args: unknown[], observers?: ReadonlySet<ApiRequestObserver>): Promise<unknown> {\n const record: MutableRequestRecord = { requestId: core.context.get().requestId ?? core.id.uuid(), procedure: path.join('.'), timestamp: Date.now(), duration: 0, success: false, attempts: 0 }\n const provided = args[1] && typeof args[1] === 'object' ? args[1] as Record<string, unknown> : {}\n const providedContext = provided.context && typeof provided.context === 'object' ? provided.context : {}\n const options = { ...provided, context: { ...providedContext, [REQUEST_OBSERVATION]: record } }\n let result = await callProcedure(rawClient, path, [args[0], options])\n record.duration = Math.max(0, Date.now() - record.timestamp)\n record.success = !(result && typeof result === 'object' && 'success' in result && result.success === false)\n if (result && typeof result === 'object' && 'error' in result && result.error && typeof result.error === 'object' && 'code' in result.error) {\n record.errorCode = String(result.error.code)\n if ('message' in result.error && typeof result.error.message === 'string') {\n const error = result.error as Parameters<typeof core.error.toPublic>[0]\n const receivedId = error.traceId && /^[\\w.:-]{1,128}$/.test(error.traceId) ? error.traceId : record.requestId\n record.requestId = receivedId\n const safe = core.error.toPublic({ ...error, publicExt: error.ext }, receivedId)\n if (error.cause && typeof error.cause === 'object' && !Array.isArray(error.cause)) {\n const metadata: Record<string, unknown> = {}\n for (const key of ['orpcCode', 'status', 'transportCode', 'direction', 'stage', 'limitBytes', 'actualBytes']) {\n const value: unknown = Reflect.get(error.cause, key)\n if (typeof value === 'number' || typeof value === 'string')\n metadata[key] = core.sanitize.sanitizeSensitiveValue(value, key)\n }\n if (Object.keys(metadata).length)\n safe.cause = metadata\n }\n core.logger.error('API request failed', { traceId: safe.traceId, requestId: record.requestId, procedure: record.procedure, status: record.status, error: result.error })\n result = { ...result, error: safe }\n }\n }\n for (const observer of observers ?? []) {\n try {\n const observed: unknown = observer(Object.freeze({ ...record }))\n if (observed instanceof Promise)\n void observed.catch(() => {})\n }\n catch { /* 观察失败不改变请求结果。 */ }\n }\n return result\n}\n\n/**\n * 解析真实 rawClient 中的 procedure 并调用,将调用异常映射为标准 HaiResult 失败。\n * @param rawClient - 已构建的 oRPC client;缺省时返回 NOT_INITIALIZED。\n * @param path - 契约层级路径。\n * @param args - 原始 procedure 参数。\n * @returns 原契约结果或客户端失败,不把失败改成成功。\n *\n * @example 模块内部上下文\n * ```ts\n * const result = await callProcedure(null, ['iam', 'auth', 'currentUser'], [])\n * // 实际得到 NOT_INITIALIZED;返回值为 unknown,外部类型来自真实契约。\n * ```\n */\nasync function callProcedure(\n rawClient: unknown,\n path: string[],\n args: unknown[],\n): Promise<unknown> {\n if (!rawClient) {\n return err(HaiApiClientError.NOT_INITIALIZED, apiClientM('apiClient_notInitialized'))\n }\n\n // 沿 path 解析 rawClient[a][b][c]...\n let target: unknown = rawClient\n for (const segment of path) {\n if ((typeof target !== 'object' || target === null) && typeof target !== 'function') {\n return err(HaiApiClientError.UNKNOWN, apiClientM('apiClient_unknown'))\n }\n target = Reflect.get(target as object, segment)\n }\n if (typeof target !== 'function') {\n return err(HaiApiClientError.UNKNOWN, apiClientM('apiClient_unknown'))\n }\n\n try {\n return await (target as (...a: unknown[]) => Promise<unknown>)(...args)\n }\n catch (error) {\n return mapClientError(error)\n }\n}\n\n// ─── 内部:构建 raw client 与 token manager ──────────────────────────────────\n\n/**\n * 仅 auth 存在时创建 TokenManager;刷新沿用自定义头、客户端标识与同一加密 fetch。\n * @param config - 已选定的客户端配置。\n * @param transport - 可选已创建的加密会话。\n * @returns 本实例管理器;没有 auth 时为 undefined,不自行启用认证。\n *\n * @example 模块内部上下文\n * ```ts\n * const manager = buildTokenManager({ baseUrl: 'https://api.example.com/api/v1', auth: { storage: createMemoryTokenStorage() }, headers: { 'x-app-channel': 'web' } }, undefined)\n * await manager?.clear()\n * ```\n */\nfunction buildTokenManager(config: ApiClientConfig, transport: TransportClient | undefined): TokenManager | undefined {\n if (!config.auth)\n return undefined\n\n const fetchFn = transport?.encryptedFetch ?? config.fetch ?? globalThis.fetch.bind(globalThis)\n // 默认使用 httpOnly cookie 存储(推荐方案:refresh token 由服务端管理,防 XSS)。\n // SSR / Node.js 测试场景请显式传入 `apiClient.tokenStorage.memory()`。\n // 原生客户端显式传入平台安全存储,浏览器不持久化 Token 到 Web Storage。\n const storage = config.auth.storage ?? createHttpOnlyCookieTokenStorage()\n const baseUrl = config.baseUrl.replace(TRAILING_SLASHES_REGEX, '')\n const refreshPath = config.auth.refreshPath ?? DEFAULT_REFRESH_PATH\n const refreshUrl = `${baseUrl}${refreshPath.startsWith('/') ? refreshPath : `/${refreshPath}`}`\n\n // 刷新沿用客户端标识与自定义头(包括原生 body 通道标识),并复用同一加密 fetch。\n const refreshFetch: typeof fetch = async (input, init) => {\n const configured = typeof config.headers === 'function' ? await config.headers() : config.headers ?? {}\n const headers = new Headers(configured)\n if (!headers.has('accept-language'))\n headers.set('accept-language', core.i18n.getGlobalLocale())\n headers.set('x-request-client', config.clientName ?? DEFAULT_CLIENT_NAME)\n new Headers(init?.headers).forEach((value, name) => headers.set(name, value))\n return fetchFn(input, { ...init, headers })\n }\n const manager = createTokenManager(storage, refreshUrl, refreshFetch, config.auth.recoverSession ? undefined : config.auth.onRefreshFailed, config.auth.refreshTimeoutMs)\n if (config.auth.onTokenRefreshed) {\n manager.onTokenRefreshed(config.auth.onTokenRefreshed)\n }\n return manager\n}\n\n/**\n * 根据显式 transport 配置创建会话;首次 encryptedFetch 才进行实际密钥协商。\n * @param config - 客户端配置及已初始化 crypto。\n * @returns 新会话或 undefined;调用方在失败/close 时 destroy。\n *\n * @example 模块内部上下文\n * ```ts\n * const session = buildTransportSession({ baseUrl: 'https://api.example.com/api/v1' })\n * // 未配置 transport 时不产生会话;正常明文请求仍由实际 fetch 发送。\n * session?.destroy()\n * ```\n */\nfunction buildTransportSession(config: ApiClientConfig): TransportClient | undefined {\n if (!config.transport)\n return undefined\n const baseFetch = config.fetch ?? globalThis.fetch.bind(globalThis)\n const baseUrl = config.baseUrl.replace(TRAILING_SLASHES_REGEX, '')\n const path = config.transport.keyExchangePath ?? '/_hai/key-exchange'\n const keyExchangeUrl = `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`\n return config.transport.crypto.transport.createClient({ keyExchangeUrl, fetch: baseFetch, maxPlaintextBytes: config.transport.maxPlaintextBytes })\n}\n\n/**\n * 以同一 contract 构造 OpenAPILink 与 JSON typed client,统一头组装、超时和认证重试。\n * @param contract - 实际 procedure 契约。\n * @param config - 目标地址与平台 fetch。\n * @param tokenManager - 可选 Token 管理器。\n * @param transport - 可选加密会话。\n * @returns oRPC JSON client;模块外层代理将调用异常映射为失败 HaiResult。\n *\n * @example 模块内部上下文\n * ```ts\n * const contract = apiContract.create({ iam: apiContract.iam })\n * const raw = buildRawClient(contract, { baseUrl: 'https://api.example.com/api/v1' }, undefined, undefined)\n * // 构造不发送请求;业务调用通过外层 ApiClient 代理。\n * ```\n */\nfunction buildRawClient<TContract extends AnyContractRouter>(\n contract: TContract,\n config: ApiClientConfig,\n tokenManager: TokenManager | undefined,\n transport: TransportClient | undefined,\n): JsonifiedClient<ContractRouterClient<TContract>> {\n const link = new OpenAPILink<Record<PropertyKey, unknown>>(contract, {\n url: config.baseUrl,\n headers: async (options) => {\n const headers = await buildHeaders(config, tokenManager, getObservation(options.context)?.requestId)\n // 单次操作凭据只附加到本次调用,不能放进跨并发请求共享的默认 header。\n if (typeof options.context.reauthentication === 'string')\n headers['x-hai-reauthentication'] = options.context.reauthentication\n return headers\n },\n fetch: async (request, init, options) => {\n const record = getObservation(options.context)\n if (record) {\n record.method = request.method\n const url = new URL(request.url)\n url.search = ''\n url.hash = ''\n record.url = core.sanitize.sanitizeUrl(url.toString())\n }\n const requestInit = options.context.keepalive === true ? { ...init, keepalive: true } : init\n const confirm = options.context.reauthorize\n const reauthorize = typeof confirm === 'function'\n ? async () => {\n const value: unknown = await confirm()\n return typeof value === 'string' || value === undefined ? value : null\n }\n : undefined\n const response = await fetchWithRefresh(request, requestInit, config, options.context.autoRefresh === false ? undefined : tokenManager, transport, record, reauthorize)\n if (record) {\n record.status = response.status\n const traceId = response.headers.get('x-trace-id') ?? response.headers.get('x-request-id')\n if (traceId && /^[\\w.:-]{1,128}$/.test(traceId))\n record.requestId = traceId\n }\n return response\n },\n })\n return createORPCClient<JsonifiedClient<ContractRouterClient<TContract>>>(link)\n}\n\n/** 内部 context 属性只由逻辑调用建立,不能从业务输入读取。 */\nfunction getObservation(context: Record<PropertyKey, unknown>): MutableRequestRecord | undefined {\n const record = context[REQUEST_OBSERVATION]\n return record && typeof record === 'object' && 'requestId' in record ? record as MutableRequestRecord : undefined\n}\n\n/**\n * 合并静态/动态配置头与客户端标识,读取可用 access token 后设置 Bearer 头。\n * @param config - 用户请求头与客户端标识。\n * @param tokenManager - 可选管理器;读取安全存储失败向上游传播。\n * @returns 单次请求的头快照,不改变调用方配置对象。\n *\n * @example 模块内部上下文\n * ```ts\n * const headers = await buildHeaders({ baseUrl: 'https://api.example.com/api/v1', clientName: 'orders-web', headers: () => ({ 'x-app-channel': 'web' }) }, undefined)\n * const channel = headers['x-app-channel']\n * ```\n */\nasync function buildHeaders(\n config: ApiClientConfig,\n tokenManager: TokenManager | undefined,\n requestId?: string,\n): Promise<Record<string, string>> {\n const configured = typeof config.headers === 'function' ? await config.headers() : config.headers ?? {}\n const headers = new Headers(configured)\n headers.set('x-hai-terminal', config.auth?.terminalType ?? 'web')\n if (!headers.has('accept-language'))\n headers.set('accept-language', core.i18n.getGlobalLocale())\n const accessToken = await tokenManager?.storage.getAccessToken()\n return {\n ...Object.fromEntries(headers),\n 'x-request-client': config.clientName ?? DEFAULT_CLIENT_NAME,\n 'x-request-id': requestId ?? core.context.get().requestId ?? core.id.uuid(),\n ...(accessToken ? { authorization: `Bearer ${accessToken}` } : {}),\n }\n}\n\n/**\n * 发送前克隆一次性请求体;HTTP 401 或有效失败包络的 401 才尝试刷新并最多重试一次。\n * 登录/注册/找回密码与 refresh 入口保留原始失败,不递归刷新;重试保留 body 并替换 Bearer 头。\n * @param request - 尚未消费的 Request。\n * @param init - oRPC 提供的重定向选项。\n * @param init.redirect - 可选的 fetch 重定向策略,原样传给首次请求及重试。\n * @param init.keepalive - 页面离开请求保持存活的浏览器提示,原样传递。\n * @param config - 请求与刷新配置。\n * @param tokenManager - 缺省时直接返回首次响应。\n * @param transport - 刷新与重试共用的可选加密会话。\n * @returns 最终 HTTP Response;不保证远端非幂等副作用没有发生。\n *\n * @example 模块内部上下文\n * ```ts\n * const config: ApiClientConfig = { baseUrl: 'https://api.example.com/api/v1', fetch: async () => Response.json({ success: true, data: {} }) }\n * const response = await fetchWithRefresh(new Request('https://api.example.com/api/v1/auth/me'), {}, config, undefined, undefined)\n * const status = response.status\n * ```\n */\nasync function fetchWithRefresh(\n request: Request,\n init: { redirect?: Request['redirect'], keepalive?: boolean },\n config: ApiClientConfig,\n tokenManager: TokenManager | undefined,\n transport: TransportClient | undefined,\n observation?: MutableRequestRecord,\n reauthorize?: () => Promise<string | null | undefined>,\n): Promise<Response> {\n // Fetch Request 的 body 是一次性可读流;为了支持 401 后重发 POST/PUT 等带 body 的请求,\n // 必须在首次发送前 `clone()` 一份。即使没有 401,clone 后的副本也只会被 GC 回收,无副作用。\n const retryRequest = tokenManager || reauthorize ? request.clone() : undefined\n const identityGeneration = tokenManager?.generation\n if (observation)\n observation.attempts++\n const response = await fetchWithTimeout(request, init, config, transport)\n if (!retryRequest)\n return response\n\n async function retryAuthorized(accessToken?: string): Promise<Response> {\n if (!retryRequest || retryRequest.signal.aborted)\n return response\n const generation = tokenManager?.generation\n const headers = new Headers(retryRequest.headers)\n if (headers.has('x-hai-reauthentication') || reauthorize) {\n if (!reauthorize)\n return response\n const ticket = await reauthorize()\n if (ticket === null || retryRequest.signal.aborted || tokenManager?.generation !== generation)\n return response\n headers.delete('x-hai-reauthentication')\n if (ticket)\n headers.set('x-hai-reauthentication', ticket)\n }\n const currentToken = accessToken ?? await tokenManager?.storage.getAccessToken()\n if (tokenManager?.generation !== generation)\n return response\n if (currentToken)\n headers.set('authorization', `Bearer ${currentToken}`)\n if (observation)\n observation.attempts++\n return fetchWithTimeout(new Request(retryRequest, { headers }), init, config, transport)\n }\n\n // 登录失败必须保留原始错误;refresh 本身也不能再次触发自动刷新。\n const baseUrl = config.baseUrl.replace(TRAILING_SLASHES_REGEX, '')\n const refreshPath = config.auth?.refreshPath ?? DEFAULT_REFRESH_PATH\n const excludedPaths = [...AUTH_ENTRY_PATHS, refreshPath]\n if (excludedPaths.some(path => new URL(`${baseUrl}${path.startsWith('/') ? path : `/${path}`}`).pathname === new URL(request.url).pathname))\n return response\n const failure: unknown = response.status === 401 || response.headers.get('content-type')?.includes('application/json')\n ? await response.clone().json().catch(() => undefined)\n : 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 if (code === 'hai:iam:027' && reauthorize)\n return retryAuthorized()\n if (!tokenManager)\n return response\n if (response.status !== 401 && !await isUnauthorizedResult(response))\n return response\n if (failure && typeof failure === 'object' && 'error' in failure && failure.error && typeof failure.error === 'object' && 'code' in failure.error\n && typeof failure.error.code === 'string' && failure.error.code.startsWith('hai:iam:') && !IamSessionErrorCodeSchema.safeParse(failure.error.code).success) {\n return response\n }\n if (tokenManager.generation !== identityGeneration)\n return response\n\n const tokens = await tokenManager.refresh()\n if (tokenManager.generation !== identityGeneration)\n return response\n let accessToken = tokens?.accessToken\n // refresh网络/上游故障会保留凭据,不能误判为会话失效弹窗,更不能重放结果不明的写入。\n if (!accessToken && config.auth?.recoverSession && tokenManager.sessionInvalid) {\n if (await config.auth.recoverSession()) {\n const restoredGeneration = tokenManager.generation\n accessToken = await tokenManager.storage.getAccessToken() ?? undefined\n if (tokenManager.generation !== restoredGeneration)\n return response\n }\n }\n if (!accessToken)\n return response\n\n return retryAuthorized(accessToken)\n}\n\n/**\n * 只从 HTTP 200 JSON 的有效失败 HaiResult 识别 httpStatus=401;克隆响应避免消费原正文。\n * @param response - 原始 HTTP 响应。\n * @returns 是否满足认证失效包络;非法 JSON/字段缺失留给原解析路径处理。\n *\n * @example 模块内部上下文\n * ```ts\n * const response = Response.json({ success: false, error: { code: 'hai:iam:001', message: 'Session expired', httpStatus: 401 } })\n * const unauthorized = await isUnauthorizedResult(response)\n * const originalBody = await response.json()\n * // 克隆检查没有消耗原响应正文。\n * ```\n */\nasync function isUnauthorizedResult(response: Response): Promise<boolean> {\n if (response.status !== 200 || !response.headers.get('content-type')?.includes('application/json'))\n return false\n try {\n const body: unknown = await response.clone().json()\n if (!body || typeof body !== 'object' || !('success' in body) || body.success !== false\n || !('error' in body) || !body.error || typeof body.error !== 'object') {\n return false\n }\n return 'httpStatus' in body.error && body.error.httpStatus === 401\n && 'code' in body.error && typeof body.error.code === 'string'\n && 'message' in body.error && typeof body.error.message === 'string'\n }\n catch {\n // 非 JSON/非法包络交由既有客户端解析路径报告,不掩盖为认证失败。\n return false\n }\n}\n\n/**\n * 把请求超时和调用方 signal 连接到本次 fetch;finally 清除本次超时计时器。\n * @param request - 含调用方取消信号的 Request。\n * @param init - oRPC 重定向设置。\n * @param init.redirect - 可选的 fetch 重定向策略,原样交给本次实际 fetch。\n * @param init.keepalive - 页面离开请求保持存活的浏览器提示,原样传递。\n * @param config - 超时与 fetch 配置。\n * @param transport - 可选加密 fetch,优先于原始 fetch。\n * @returns HTTP 响应;超时/取消异常由外层 procedure 映射,不能视为远端回滚。\n *\n * @example 模块内部上下文\n * ```ts\n * const request = new Request('https://api.example.com/api/v1/auth/me')\n * const response = await fetchWithTimeout(request, {}, { baseUrl: 'https://api.example.com/api/v1', timeout: 1000, fetch: async () => new Response(null, { status: 204 }) }, undefined)\n * const status = response.status\n * ```\n */\nasync function fetchWithTimeout(\n request: Request,\n init: { redirect?: Request['redirect'], keepalive?: boolean },\n config: ApiClientConfig,\n transport: TransportClient | undefined,\n): Promise<Response> {\n // 启用传输加密时,用 encryptedFetch 作为实际 fetch;\n // 它会首次请求前自动完成密钥协商,后续请求复用会话。\n const fetchFn = transport?.encryptedFetch ?? config.fetch ?? globalThis.fetch.bind(globalThis)\n const timeout = config.timeout ?? DEFAULT_TIMEOUT\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n // 保留调用方原始 signal 的中断能力:上游主动 abort 也能控制超时 controller。\n const userSignal = request.signal\n if (userSignal) {\n if (userSignal.aborted) {\n controller.abort()\n }\n else {\n userSignal.addEventListener('abort', () => controller.abort(), { once: true })\n }\n }\n try {\n return await fetchFn(new Request(request, { signal: controller.signal }), init)\n }\n finally {\n clearTimeout(timeoutId)\n }\n}\n\n/**\n * 将 oRPC 状态、AbortError 与其它传输异常映射为稳定客户端错误;保留 oRPC code/status/data 详情。\n * @param error - procedure 抛出的未知异常。\n * @returns 失败 HaiResult,不包含虚构的成功数据。\n *\n * @example 模块内部上下文\n * ```ts\n * const result = mapClientError(new DOMException('Request aborted', 'AbortError'))\n * if (!result.success) { const code = result.error.code }\n * ```\n */\nfunction mapClientError(error: unknown) {\n const time = error instanceof TransportTimeError ? error : error instanceof Error && error.cause instanceof TransportTimeError ? error.cause : undefined\n if (time)\n return err(HaiApiClientError.VALIDATION_FAILED, time.message, { transportCode: time.code })\n const capacity = error instanceof TransportBodyTooLargeError ? error : error instanceof Error && error.cause instanceof TransportBodyTooLargeError ? error.cause : undefined\n if (capacity)\n return err(HaiApiClientError.PAYLOAD_TOO_LARGE, capacity.message, { direction: capacity.direction, stage: capacity.stage, limitBytes: capacity.limitBytes, actualBytes: capacity.actualBytes })\n if (error instanceof ORPCError) {\n // Kit 等 HTTP 宿主使用非 2xx HaiResult;oRPC 将该包络放在 data.body 中。\n // 恢复已校验的业务错误,HTTP 状态优先,不能让正文把 5xx 降为可公开的 4xx。\n const body: unknown = error.data && typeof error.data === 'object' && 'body' in error.data ? error.data.body : undefined\n if (body && typeof body === 'object' && 'success' in body && body.success === false && 'error' in body) {\n const parsed = HaiErrorSchema.safeParse(body.error)\n if (parsed.success)\n return err({ ...parsed.data, httpStatus: error.status })\n }\n // 保留 ORPCError 的原始 code / status / data,供上层区分具体业务异常(不仅是 HTTP 状态码)。\n const detail = { orpcCode: error.code, status: error.status, data: error.data }\n if (error.status === 413)\n return err(HaiApiClientError.PAYLOAD_TOO_LARGE, error.message, detail)\n if (error.status === 400 || error.status === 422)\n return err(HaiApiClientError.VALIDATION_FAILED, error.message, detail)\n if (error.status === 401)\n return err(HaiApiClientError.UNAUTHORIZED, error.message, detail)\n if (error.status === 403)\n return err(HaiApiClientError.FORBIDDEN, error.message, detail)\n if (error.status === 404)\n return err(HaiApiClientError.NOT_FOUND, error.message, detail)\n return err(HaiApiClientError.SERVER_ERROR, error.message, detail)\n }\n if (error instanceof DOMException && error.name === 'AbortError') {\n return err(HaiApiClientError.TIMEOUT, apiClientM('apiClient_timeout'), error)\n }\n return err(HaiApiClientError.NETWORK_ERROR, apiClientM('apiClient_networkError'), error)\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/api-client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.0.0-beta.1",
|
|
5
5
|
"description": "Hai Framework universal HTTP client with Bearer token and API contract support.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"exports": {
|
|
@@ -19,25 +19,34 @@
|
|
|
19
19
|
"publishConfig": {
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": "^22.23.4 || ^24.22.1 || ^26.12.1"
|
|
24
|
+
},
|
|
22
25
|
"dependencies": {
|
|
23
|
-
"
|
|
24
|
-
"@h-ai/core": "
|
|
26
|
+
"@h-ai/api-contract": "1.0.0-beta.1",
|
|
27
|
+
"@h-ai/core": "1.0.0-beta.1",
|
|
28
|
+
"@h-ai/crypto": "1.0.0-beta.1",
|
|
29
|
+
"@orpc/client": "^1.15.5",
|
|
30
|
+
"@orpc/contract": "^1.15.5",
|
|
31
|
+
"@orpc/openapi-client": "^1.15.5",
|
|
32
|
+
"zod": "^4.6.5"
|
|
25
33
|
},
|
|
26
34
|
"devDependencies": {
|
|
27
|
-
"@types/node": "^
|
|
28
|
-
"rimraf": "^6.
|
|
29
|
-
"tsup": "^8.
|
|
30
|
-
"typescript": "^
|
|
31
|
-
"vitest": "^4.
|
|
35
|
+
"@types/node": "^26.6.5",
|
|
36
|
+
"rimraf": "^6.1.3",
|
|
37
|
+
"tsup": "^8.5.1",
|
|
38
|
+
"typescript": "^6.0.3",
|
|
39
|
+
"vitest": "^4.1.11"
|
|
32
40
|
},
|
|
33
41
|
"scripts": {
|
|
34
42
|
"build": "tsup",
|
|
35
43
|
"dev": "tsup --watch",
|
|
36
44
|
"test": "vitest run",
|
|
37
45
|
"test:watch": "vitest",
|
|
38
|
-
"typecheck": "
|
|
46
|
+
"typecheck": "node ../../scripts/typecheck.mjs --noEmit",
|
|
39
47
|
"lint": "eslint .",
|
|
40
48
|
"lint:fix": "eslint . --fix",
|
|
41
|
-
"clean": "rimraf dist .turbo"
|
|
49
|
+
"clean": "rimraf dist .turbo",
|
|
50
|
+
"build:clean": "rimraf dist"
|
|
42
51
|
}
|
|
43
52
|
}
|