dsh-lh-data 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +332 -0
- package/cordis.patch.yml +7 -0
- package/lib/admin-contract.d.ts +274 -0
- package/lib/admin-http.d.ts +31 -0
- package/lib/admin-validate.d.ts +87 -0
- package/lib/admin.d.ts +60 -0
- package/lib/client.js +2969 -0
- package/lib/client.js.map +1 -0
- package/lib/datasource/columns.d.ts +13 -0
- package/lib/datasource/connection.d.ts +33 -0
- package/lib/datasource/connector/base.d.ts +21 -0
- package/lib/datasource/connector/mysql.d.ts +23 -0
- package/lib/datasource/connector/postgresql.d.ts +23 -0
- package/lib/datasource/crypto.d.ts +13 -0
- package/lib/datasource/driver.d.ts +39 -0
- package/lib/datasource/errors.d.ts +25 -0
- package/lib/datasource/importer.d.ts +38 -0
- package/lib/datasource/source-sql.d.ts +11 -0
- package/lib/datasource/source-store.d.ts +28 -0
- package/lib/datasource/types.d.ts +119 -0
- package/lib/db.d.ts +91 -0
- package/lib/http-common.d.ts +31 -0
- package/lib/http.d.ts +13 -0
- package/lib/index.d.ts +50 -0
- package/lib/index.js +5735 -0
- package/lib/parse.d.ts +41 -0
- package/lib/preview.d.ts +75 -0
- package/lib/render.d.ts +100 -0
- package/lib/scope-registry.d.ts +47 -0
- package/lib/scope.d.ts +56 -0
- package/lib/sql.d.ts +133 -0
- package/lib/store.d.ts +174 -0
- package/lib/table.d.ts +38 -0
- package/lib/tooling.d.ts +267 -0
- package/lib/tools/datasource.d.ts +16 -0
- package/lib/tools/import.d.ts +21 -0
- package/lib/tools/read.d.ts +77 -0
- package/lib/tools/registry.d.ts +12 -0
- package/lib/tools/write.d.ts +37 -0
- package/lib/view.d.ts +138 -0
- package/package.json +61 -0
- package/src/admin-contract.ts +351 -0
- package/src/admin-http.ts +254 -0
- package/src/admin-validate.ts +393 -0
- package/src/admin.ts +558 -0
- package/src/client/index.ts +403 -0
- package/src/client/settings/CreateForm.tsx +186 -0
- package/src/client/settings/DataSourceForm.tsx +278 -0
- package/src/client/settings/DataSourcesPanel.tsx +301 -0
- package/src/client/settings/DatasetEditor.tsx +245 -0
- package/src/client/settings/DatasetTable.tsx +110 -0
- package/src/client/settings/DatasetsPanel.tsx +207 -0
- package/src/client/settings/RowsPanel.tsx +196 -0
- package/src/client/settings/Section.tsx +41 -0
- package/src/client/settings/SourceTablesPanel.tsx +226 -0
- package/src/client/settings/api.ts +154 -0
- package/src/client/settings/styles.ts +125 -0
- package/src/datasource/columns.ts +56 -0
- package/src/datasource/connection.ts +124 -0
- package/src/datasource/connector/base.ts +54 -0
- package/src/datasource/connector/mysql.ts +187 -0
- package/src/datasource/connector/postgresql.ts +212 -0
- package/src/datasource/crypto.ts +61 -0
- package/src/datasource/driver.ts +108 -0
- package/src/datasource/errors.ts +69 -0
- package/src/datasource/importer.ts +262 -0
- package/src/datasource/index.ts +62 -0
- package/src/datasource/source-sql.ts +64 -0
- package/src/datasource/source-store.ts +152 -0
- package/src/datasource/types.ts +133 -0
- package/src/db.ts +277 -0
- package/src/http-common.ts +91 -0
- package/src/http.ts +130 -0
- package/src/index.ts +486 -0
- package/src/parse.ts +213 -0
- package/src/preview.ts +198 -0
- package/src/render.ts +294 -0
- package/src/scope-registry.ts +111 -0
- package/src/scope.ts +159 -0
- package/src/sql.ts +491 -0
- package/src/store.ts +412 -0
- package/src/table.ts +160 -0
- package/src/tooling.ts +551 -0
- package/src/tools/datasource.ts +378 -0
- package/src/tools/import.ts +282 -0
- package/src/tools/read.ts +536 -0
- package/src/tools/registry.ts +56 -0
- package/src/tools/write.ts +241 -0
- package/src/view.ts +371 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["ADMIN_API_BASE: string","ADMIN_SOURCE_TYPES: readonly string[]","ADMIN_SOURCE_TYPE_LABELS: Readonly<Record<string, string>>","ADMIN_SOURCE_DEFAULT_PORTS: Readonly<Record<string, number>>","ADMIN_COLUMN_TYPE_LABELS: Readonly<Record<AdminColumnType, string>>","ADMIN_COLUMN_TYPES: readonly AdminColumnType[]","status: number","code: string","message: string","path: string","init?: RequestInit","query: string","page: number","pageSize: number","id: string","scope: string","body: CreateDatasetRequest","body: PatchDatasetRequest","body: CreateDataSourceRequest","body: PatchDataSourceRequest","body: TestDataSourceRequest","schema?: string","query?: string","body: ImportSourceTableRequest","STATUS_COLOR: Record<string, string>","SOURCE_STATUS_COLOR: Record<string, string>","SOURCE_STATUS_TEXT: Record<string, string>","TYPE_BADGE: Record<string, string>","STATUS_TEXT: Record<string, string>","ts: number","value: string | null","props: DatasetTableProps","sample: unknown[]","text: string","columns: DatasetDetailView['columns']","drafts: Record<string, ColumnDraft>","props: DatasetEditorProps","key: string","patch: Partial<ColumnDraft>","patches: DatasetColumnPatch[]","patch: DatasetColumnPatch","body: PatchDatasetRequest","value: unknown","props: { value: unknown }","props: RowsPanelProps","target: number","size: number","EMPTY_COLUMN: ColumnDraft","props: CreateFormProps","index: number","patch: Partial<ColumnDraft>","q: string","p: number","item: DatasetAdminView","failure: Error","updated: DatasetDetailView","id: string","created: unknown","props: DataSourceFormProps","next: () => void","count: number","props: SourceTablesPanelProps","schemaArg: string","q: string","id: string","view: DataSourceView","failure: Error","TABS: { key: Tab; label: string }[]","value: unknown","cells: unknown[]","columns: string[]","rows: Record<string, unknown>[]","name: string","now: Date","value: number","filename: string","text: string","props: ToolCallViewProps","column: string","chunks: string[]","ctx: ClientContext"],"sources":["../src/admin-contract.ts","../src/client/settings/api.ts","../src/client/settings/styles.ts","../src/client/settings/DatasetTable.tsx","../src/client/settings/DatasetEditor.tsx","../src/client/settings/RowsPanel.tsx","../src/client/settings/CreateForm.tsx","../src/client/settings/DatasetsPanel.tsx","../src/client/settings/DataSourceForm.tsx","../src/client/settings/SourceTablesPanel.tsx","../src/client/settings/DataSourcesPanel.tsx","../src/client/settings/Section.tsx","../src/client/index.ts"],"sourcesContent":["/**\r\n * 设置页管理接口的**双半身契约**。\r\n *\r\n * 主机半身(`admin.ts` / `admin-http.ts`)与浏览器半身(`client/settings/*`)\r\n * 共用本文件,因此有两条硬约束:\r\n *\r\n * 1. **禁止 import 任何 node 内置模块** —— 本文件会被打进浏览器 bundle\r\n * (`tsdown.config.ts` 的 client 产物);\r\n * 2. **只有常量与类型**,不含任何运行时逻辑 —— 浏览器 bundle 是 CJS 工厂包,\r\n * 多引一个模块就多一份内联体积。\r\n *\r\n * 存在理由:浏览器拿不到主机的 `Config`。`viewRoutePrefix` 之类是用户在 dsh\r\n * 配置里改的,如果路由前缀两边各写一份字面量,改配置就会让设置页悄悄失联。\r\n * 下面的常量是唯一真源,主机注册路由和浏览器拼 URL 都从这里取。\r\n */\r\n\r\n// ── 路由 ─────────────────────────────────────────────────────────────────\r\n\r\n/** 管理接口的路由前缀。与视图接口(可配置)解耦,固定不变。 */\r\nexport const ADMIN_ROUTE_PREFIX = '/api/lh-data'\r\n\r\n/** 管理接口的固定挂载点:`${ADMIN_ROUTE_PREFIX}/admin`。 */\r\nexport const ADMIN_API_BASE: string = `${ADMIN_ROUTE_PREFIX}/admin`\r\n\r\n// ── 设置菜单 ─────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * `settings.section` 的注册 id。既有分区用的是 0(general)/ 10(models)/\r\n * 15(plugins)/ 20(agent-presets),这里取 100 排在最后。\r\n */\r\nexport const ADMIN_SECTION_ID = 'lh-data'\r\nexport const ADMIN_SECTION_ORDER = 100\r\nexport const ADMIN_SECTION_LABEL = '数据集'\r\n\r\n// ── 列表分页 ─────────────────────────────────────────────────────────────\r\n\r\nexport const ADMIN_PAGE_SIZE = 20\r\nexport const ADMIN_MAX_PAGE_SIZE = 100\r\n\r\n// ── 表数据分页(查看某个数据集的行) ───────────────────────────────────────\r\n\r\n/** 表数据默认每页行数。 */\r\nexport const ADMIN_ROW_PAGE_SIZE = 20\r\n/** 表数据每页行数的上限。 */\r\nexport const ADMIN_ROW_MAX_PAGE_SIZE = 100\r\n/** 行号列:物理表的自增主键,只用于稳定排序与前端行键,不算业务列。 */\r\nexport const ADMIN_ROW_ID_COLUMN = '_row_id'\r\n\r\n// ── 录入上限(前端 maxLength 与后端校验共用) ─────────────────────────────\r\n\r\nexport const ADMIN_MAX_NAME_LENGTH = 80\r\nexport const ADMIN_MAX_DESCRIPTION_LENGTH = 500\r\nexport const ADMIN_MAX_SOURCE_LENGTH = 1024\r\nexport const ADMIN_MAX_COLUMNS = 64\r\nexport const ADMIN_MAX_QUERY_LENGTH = 80\r\n/** 单列样例值的个数上限(前端拼接与后端夹紧共用)。 */\r\nexport const ADMIN_MAX_SAMPLE_VALUES = 5\r\n/** 单个样例值的字符上限。 */\r\nexport const ADMIN_MAX_SAMPLE_LENGTH = 40\r\n\r\n// ── 数据源录入上限 ───────────────────────────────────────────────────────\r\n\r\nexport const ADMIN_MAX_SOURCE_NAME_LENGTH = 80\r\nexport const ADMIN_MAX_HOST_LENGTH = 255\r\nexport const ADMIN_MAX_DATABASE_LENGTH = 128\r\nexport const ADMIN_MAX_USERNAME_LENGTH = 128\r\nexport const ADMIN_MAX_PASSWORD_LENGTH = 256\r\nexport const ADMIN_MIN_PORT = 1\r\nexport const ADMIN_MAX_PORT = 65535\r\nexport const ADMIN_MAX_POOL_MAX = 100\r\n\r\n/** 数据源类型下拉的可选项(顺序即展示顺序)。 */\r\nexport const ADMIN_SOURCE_TYPES: readonly string[] = ['mysql', 'postgresql']\r\n\r\nexport const ADMIN_SOURCE_TYPE_LABELS: Readonly<Record<string, string>> = {\r\n mysql: 'MySQL',\r\n postgresql: 'PostgreSQL',\r\n}\r\n\r\n/** 各类型的默认端口(前端带出与后端兜底共用)。 */\r\nexport const ADMIN_SOURCE_DEFAULT_PORTS: Readonly<Record<string, number>> = {\r\n mysql: 3306,\r\n postgresql: 5432,\r\n}\r\n\r\n// ── 类型 ─────────────────────────────────────────────────────────────────\r\n\r\n/** 与 `parse.ts` 的 `ColumnType` 同构(本文件不能 import 主机侧的 parse)。 */\r\nexport type AdminColumnType = 'text' | 'numeric' | 'boolean' | 'date'\r\n\r\n/** 人类可读的类型名:前端下拉与列结构表共用,避免两处各写一份中文。 */\r\nexport const ADMIN_COLUMN_TYPE_LABELS: Readonly<Record<AdminColumnType, string>> = {\r\n text: '文本',\r\n numeric: '数值',\r\n boolean: '布尔',\r\n date: '日期',\r\n}\r\n\r\n/** 列类型下拉的可选项(顺序即展示顺序)。 */\r\nexport const ADMIN_COLUMN_TYPES: readonly AdminColumnType[] = ['text', 'numeric', 'boolean', 'date']\r\n\r\nexport type DatasetStatus = 'importing' | 'ready' | 'failed'\r\n\r\n/** 设置页可见的一列。列名是业务表头(可含中文),不含任何物理表信息。 */\r\nexport interface DatasetColumnView {\r\n /** 原始表头。 */\r\n name: string\r\n /** SQL 中使用的列名(重名时追加 `_2`)。 */\r\n sanitizedName: string\r\n type: AdminColumnType\r\n nullable: boolean\r\n description: string\r\n sample: unknown[]\r\n}\r\n\r\n/** 新建数据集时提交的一列。 */\r\nexport interface DatasetColumnSpec {\r\n name: string\r\n type: AdminColumnType\r\n nullable?: boolean\r\n description?: string\r\n}\r\n\r\n/** 列表里的一条数据集(跨工作区聚合;不含列结构,避免列表响应膨胀)。 */\r\nexport interface DatasetAdminView {\r\n id: string\r\n scopeKey: string\r\n name: string\r\n description: string | null\r\n sourcePath: string | null\r\n rowCount: number\r\n columnCount: number\r\n status: DatasetStatus\r\n error: string | null\r\n createdAt: number\r\n updatedAt: number\r\n}\r\n\r\n/** 详情 = 列表项 + 只读列结构。 */\r\nexport interface DatasetDetailView extends DatasetAdminView {\r\n columns: DatasetColumnView[]\r\n}\r\n\r\n/**\r\n * 一页表数据(只读)。`columns` 只含业务列,`rows` 每行额外带 `_row_id`\r\n * (`ADMIN_ROW_ID_COLUMN`)作为稳定行键;`page` 由服务端夹到分页边界内。\r\n */\r\nexport interface DatasetRowsResult {\r\n columns: string[]\r\n rows: Record<string, unknown>[]\r\n page: number\r\n pageSize: number\r\n /** `COUNT(*)` 得到的真实行数(元数据里的 rowCount 可能与之不符)。 */\r\n total: number\r\n totalPages: number\r\n}\r\n\r\n/** 设置页可见的一个工作区。 */\r\nexport interface ScopeView {\r\n scopeKey: string\r\n lastSeen: number\r\n}\r\n\r\n/** 单个工作区读取失败时的降级提示(不阻断整页)。 */\r\nexport interface AdminWarning {\r\n scopeKey: string\r\n message: string\r\n}\r\n\r\nexport interface ListDatasetsResult {\r\n items: DatasetAdminView[]\r\n total: number\r\n page: number\r\n pageSize: number\r\n /** 命中 `adminMaxDatasets` 上限被截断。 */\r\n truncated: boolean\r\n warnings: AdminWarning[]\r\n}\r\n\r\n/** 新建空数据集:列名 / 类型 / 可空性提交后冻结,说明与样例仍可改。 */\r\nexport interface CreateDatasetRequest {\r\n scopeKey: string\r\n name: string\r\n description?: string\r\n sourcePath?: string | null\r\n columns: DatasetColumnSpec[]\r\n}\r\n\r\n/**\r\n * 列定义的补丁:只改「说明」与「样例」这两项纯元数据。\r\n * 列名 / 类型 / 可空性对应物理表 DDL,设置页不可改。\r\n * 用 `sanitizedName` 定位列——原始表头 `name` 在重名列上会重复,不能当键。\r\n */\r\nexport interface DatasetColumnPatch {\r\n /** 列的 SQL 名(去重后唯一)。 */\r\n sanitizedName: string\r\n description?: string\r\n sample?: unknown[]\r\n}\r\n\r\n// ── 数据源 ───────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * 设置页可见的一条数据源。\r\n * **密码永不回显**:只给 `hasPassword`,前端据此显示「已设置 · 不可查看」。\r\n */\r\nexport interface DataSourceView {\r\n id: string\r\n name: string\r\n type: string\r\n host: string\r\n port: number\r\n database: string\r\n username: string\r\n hasPassword: boolean\r\n sslMode: string | null\r\n poolMax: number | null\r\n description: string | null\r\n status: string\r\n lastError: string | null\r\n lastCheckedAt: number | null\r\n createdAt: number\r\n updatedAt: number\r\n}\r\n\r\nexport interface ConnectionTestView {\r\n success: boolean\r\n latency: number\r\n version: string | null\r\n error: string | null\r\n}\r\n\r\n/** 新建数据源。`test` 为 true 时先测连,不通就不落库。 */\r\nexport interface CreateDataSourceRequest {\r\n name: string\r\n type: string\r\n host: string\r\n port?: number | null\r\n database: string\r\n username: string\r\n password?: string\r\n sslMode?: string | null\r\n poolMax?: number | null\r\n description?: string | null\r\n test?: boolean\r\n}\r\n\r\n/** 改数据源:字段缺省表示不改;`password` 给了字符串就替换(空串表示置空)。 */\r\nexport interface PatchDataSourceRequest {\r\n name?: string\r\n type?: string\r\n host?: string\r\n port?: number | null\r\n database?: string\r\n username?: string\r\n password?: string\r\n sslMode?: string | null\r\n poolMax?: number | null\r\n description?: string | null\r\n test?: boolean\r\n}\r\n\r\n/** `POST /sources/test` 的请求体:给 `source` 就测已登记的,给全参数就测未保存的草稿。 */\r\nexport interface TestDataSourceRequest {\r\n source?: string\r\n type?: string\r\n host?: string\r\n port?: number | null\r\n database?: string\r\n username?: string\r\n password?: string\r\n sslMode?: string | null\r\n poolMax?: number | null\r\n}\r\n\r\nexport interface SourceTableView {\r\n tableName: string\r\n schemaName: string | null\r\n /** 远端估计行数;-1 表示未知。 */\r\n rowCount: number\r\n primaryKey: string | null\r\n columns: {\r\n name: string\r\n type: AdminColumnType\r\n nullable: boolean\r\n description: string | null\r\n }[]\r\n}\r\n\r\nexport interface ListSourceTablesResult {\r\n /** 实际使用的 schema(MySQL 为库名,PostgreSQL 默认 public)。 */\r\n schema: string | null\r\n /** 可选 schema 列表(PostgreSQL 多个,MySQL 一个)。 */\r\n schemas: string[]\r\n tables: SourceTableView[]\r\n}\r\n\r\n/** `POST /sources/:id/import`。 */\r\nexport interface ImportSourceTableRequest {\r\n scopeKey: string\r\n tableName: string\r\n schemaName?: string | null\r\n name?: string | null\r\n limit?: number | null\r\n}\r\n\r\nexport interface ImportSourceTableResult {\r\n datasetId: string\r\n name: string\r\n rowCount: number\r\n columnCount: number\r\n status: 'ready' | 'running'\r\n jobId?: string\r\n}\r\n\r\n/** 字段缺省表示不改动。 */\r\nexport interface PatchDatasetRequest {\r\n name?: string\r\n description?: string | null\r\n sourcePath?: string | null\r\n columns?: DatasetColumnPatch[]\r\n}\r\n\r\nexport type AdminErrorCode =\r\n | 'UNAUTHORIZED'\r\n | 'FORBIDDEN'\r\n | 'NOT_FOUND'\r\n | 'METHOD_NOT_ALLOWED'\r\n | 'BAD_REQUEST'\r\n | 'PAYLOAD_TOO_LARGE'\r\n | 'READ_ONLY'\r\n | 'ADMIN_DISABLED'\r\n | 'SCOPE_UNKNOWN'\r\n | 'DUPLICATE_NAME'\r\n | 'INVALID_COLUMNS'\r\n | 'QUERY_FAILED'\r\n | 'SOURCE_DISABLED'\r\n | 'DRIVER_MISSING'\r\n | 'SOURCE_UNREACHABLE'\r\n | 'IMPORT_FAILED'\r\n\r\n/** 错误响应体。主机侧统一脱敏:不回显 SQL 与物理表名。 */\r\nexport interface AdminErrorBody {\r\n error: { code: AdminErrorCode; message: string }\r\n}\r\n\r\n/** 管理接口是否可写(前端据此置灰按钮)。 */\r\nexport interface AdminCapabilities {\r\n writable: boolean\r\n reason: string\r\n}\r\n","/**\r\n * 设置页管理接口的浏览器侧取数封装。\r\n *\r\n * 全部走同源 `fetch`,前缀来自 `admin-contract` 的 `ADMIN_API_BASE`(与主机侧同一真源,\r\n * 避免客户端硬编码漂移)。错误统一收口成 `AdminApiError`,携带服务端的中文 message,\r\n * 由组件层直接展示。\r\n */\r\n\r\nimport {\r\n ADMIN_API_BASE,\r\n type ConnectionTestView,\r\n type CreateDataSourceRequest,\r\n type CreateDatasetRequest,\r\n type DataSourceView,\r\n type DatasetAdminView,\r\n type DatasetDetailView,\r\n type DatasetRowsResult,\r\n type ImportSourceTableRequest,\r\n type ImportSourceTableResult,\r\n type ListDatasetsResult,\r\n type ListSourceTablesResult,\r\n type PatchDataSourceRequest,\r\n type PatchDatasetRequest,\r\n type ScopeView,\r\n type TestDataSourceRequest,\r\n} from '../../admin-contract'\r\n\r\n/** 服务端脱敏后的错误(含中文 message 与机器码)。 */\r\nexport class AdminApiError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n public readonly code: string,\r\n message: string,\r\n ) {\r\n super(message)\r\n this.name = 'AdminApiError'\r\n }\r\n}\r\n\r\nasync function request<T>(path: string, init?: RequestInit): Promise<T> {\r\n const response = await fetch(`${ADMIN_API_BASE}${path}`, {\r\n ...init,\r\n credentials: 'same-origin',\r\n headers: { 'content-type': 'application/json', ...(init?.headers ?? {}) },\r\n })\r\n if (!response.ok) {\r\n let code = 'UNKNOWN'\r\n let message = `请求失败(HTTP ${response.status})`\r\n try {\r\n const body = await response.json() as { error?: { code?: string; message?: string } }\r\n if (body.error) {\r\n code = body.error.code ?? code\r\n message = body.error.message ?? message\r\n }\r\n } catch {\r\n // 响应体非 JSON:保留默认文案。\r\n }\r\n throw new AdminApiError(response.status, code, message)\r\n }\r\n return (await response.json()) as T\r\n}\r\n\r\nexport function listScopes(): Promise<{ scopes: ScopeView[] }> {\r\n return request('/scopes')\r\n}\r\n\r\nexport function listDatasets(query: string, page: number, pageSize: number): Promise<ListDatasetsResult> {\r\n const params = new URLSearchParams()\r\n if (query.trim().length > 0) params.set('q', query)\r\n params.set('page', String(page))\r\n params.set('pageSize', String(pageSize))\r\n return request(`/datasets?${params.toString()}`)\r\n}\r\n\r\nexport function getDataset(id: string, scope: string): Promise<DatasetDetailView> {\r\n return request(`/datasets/${encodeURIComponent(id)}?scope=${encodeURIComponent(scope)}`)\r\n}\r\n\r\n/** 分页读取数据集的物理表数据(只读)。 */\r\nexport function listDatasetRows(\r\n id: string,\r\n scope: string,\r\n page: number,\r\n pageSize: number,\r\n): Promise<DatasetRowsResult> {\r\n const params = new URLSearchParams()\r\n params.set('scope', scope)\r\n params.set('page', String(page))\r\n params.set('pageSize', String(pageSize))\r\n return request(`/datasets/${encodeURIComponent(id)}/rows?${params.toString()}`)\r\n}\r\n\r\nexport function createDataset(body: CreateDatasetRequest): Promise<DatasetDetailView> {\r\n return request('/datasets', { method: 'POST', body: JSON.stringify(body) })\r\n}\r\n\r\nexport function patchDataset(\r\n id: string,\r\n scope: string,\r\n body: PatchDatasetRequest,\r\n): Promise<DatasetDetailView> {\r\n return request(`/datasets/${encodeURIComponent(id)}?scope=${encodeURIComponent(scope)}`, {\r\n method: 'PATCH',\r\n body: JSON.stringify(body),\r\n })\r\n}\r\n\r\nexport function deleteDataset(\r\n id: string,\r\n scope: string,\r\n): Promise<{ id: string; name: string; dropped: boolean }> {\r\n return request(`/datasets/${encodeURIComponent(id)}?scope=${encodeURIComponent(scope)}`, {\r\n method: 'DELETE',\r\n })\r\n}\r\n\r\n// ── 数据源 ───────────────────────────────────────────────────────────────\r\n\r\nexport function listDataSources(): Promise<{ sources: DataSourceView[] }> {\r\n return request('/sources')\r\n}\r\n\r\nexport function getDataSource(id: string): Promise<DataSourceView> {\r\n return request(`/sources/${encodeURIComponent(id)}`)\r\n}\r\n\r\nexport function createDataSource(body: CreateDataSourceRequest): Promise<DataSourceView> {\r\n return request('/sources', { method: 'POST', body: JSON.stringify(body) })\r\n}\r\n\r\nexport function patchDataSource(id: string, body: PatchDataSourceRequest): Promise<DataSourceView> {\r\n return request(`/sources/${encodeURIComponent(id)}`, { method: 'PATCH', body: JSON.stringify(body) })\r\n}\r\n\r\nexport function deleteDataSource(id: string): Promise<{ deleted: boolean }> {\r\n return request(`/sources/${encodeURIComponent(id)}`, { method: 'DELETE' })\r\n}\r\n\r\n/** 测已登记的数据源(给 source)或未保存的草稿(给完整参数)。 */\r\nexport function testDataSource(body: TestDataSourceRequest): Promise<ConnectionTestView> {\r\n return request('/sources/test', { method: 'POST', body: JSON.stringify(body) })\r\n}\r\n\r\nexport function listSourceTables(id: string, schema?: string, query?: string): Promise<ListSourceTablesResult> {\r\n const params = new URLSearchParams()\r\n if (schema !== undefined && schema.trim().length > 0) params.set('schema', schema)\r\n if (query !== undefined && query.trim().length > 0) params.set('q', query)\r\n const suffix = params.toString().length > 0 ? `?${params.toString()}` : ''\r\n return request(`/sources/${encodeURIComponent(id)}/tables${suffix}`)\r\n}\r\n\r\nexport function importSourceTable(id: string, body: ImportSourceTableRequest): Promise<ImportSourceTableResult> {\r\n return request(`/sources/${encodeURIComponent(id)}/import`, { method: 'POST', body: JSON.stringify(body) })\r\n}\r\n","/**\r\n * 设置页的内联样式常量。沿用插件既有风格:中性灰底、细边框、6px 圆角、12–13px 字号,\r\n * 全部走 `var(--dsw-*)` 设计变量并带字面量兜底,自动跟随平台明暗主题。\r\n * 不引入任何 CSS 管线(与浏览器半身既有卡片一致)。\r\n */\r\n\r\nexport const c = {\r\n bg: 'var(--dsw-bg, #ffffff)',\r\n subtle: 'var(--dsw-bg-subtle, #fafafa)',\r\n fg: 'var(--dsw-fg, #1f2328)',\r\n fg2: 'var(--dsw-fg-secondary, #656d76)',\r\n border: 'var(--dsw-border, #e5e5e5)',\r\n primary: 'var(--dsw-primary, #2f6feb)',\r\n danger: 'var(--dsw-danger, #c0392b)',\r\n ok: 'var(--dsw-ok, #1a7f37)',\r\n warn: 'var(--dsw-warn, #9a6700)',\r\n} as const\r\n\r\nexport const s = {\r\n page: { fontSize: 12, color: c.fg, lineHeight: 1.6 },\r\n toolbar: {\r\n display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10,\r\n paddingBottom: 10, borderBottom: `1px solid ${c.border}`,\r\n },\r\n search: {\r\n flex: 1, minWidth: 0, padding: '5px 8px', fontSize: 12,\r\n border: `1px solid ${c.border}`, borderRadius: 6, background: c.bg, color: c.fg,\r\n outline: 'none',\r\n },\r\n button: {\r\n border: `1px solid ${c.border}`, background: c.bg, color: c.fg,\r\n borderRadius: 6, padding: '5px 10px', cursor: 'pointer', fontSize: 12, whiteSpace: 'nowrap',\r\n },\r\n primaryButton: {\r\n border: `1px solid ${c.primary}`, background: c.primary, color: '#fff',\r\n borderRadius: 6, padding: '5px 12px', cursor: 'pointer', fontSize: 12, whiteSpace: 'nowrap',\r\n },\r\n dangerButton: {\r\n border: `1px solid ${c.danger}`, background: 'transparent', color: c.danger,\r\n borderRadius: 6, padding: '5px 10px', cursor: 'pointer', fontSize: 12,\r\n },\r\n disabledButton: { opacity: 0.5, cursor: 'not-allowed' },\r\n banner: {\r\n marginBottom: 10, padding: '6px 10px', borderRadius: 6,\r\n border: `1px solid ${c.danger}`, color: c.danger, fontSize: 12,\r\n display: 'flex', alignItems: 'center', gap: 8,\r\n },\r\n scroll: {\r\n maxHeight: 360, overflow: 'auto', border: `1px solid ${c.border}`, borderRadius: 6,\r\n },\r\n table: { borderCollapse: 'collapse', width: '100%', fontSize: 12 },\r\n th: {\r\n position: 'sticky', top: 0, background: c.subtle, textAlign: 'left',\r\n padding: '6px 8px', borderBottom: `1px solid ${c.border}`, color: c.fg2, fontWeight: 600,\r\n whiteSpace: 'nowrap',\r\n },\r\n td: {\r\n padding: '5px 8px', borderBottom: `1px solid ${c.border}`, maxWidth: 280,\r\n overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',\r\n },\r\n row: { cursor: 'pointer' },\r\n selectedRow: { boxShadow: `inset 2px 0 0 ${c.primary}` },\r\n rowHover: { background: c.subtle },\r\n dot: { display: 'inline-block', width: 7, height: 7, borderRadius: '50%', marginRight: 5 },\r\n panel: {\r\n marginTop: 12, padding: 12, border: `1px solid ${c.border}`, borderRadius: 6, background: c.subtle,\r\n },\r\n field: { marginBottom: 10 },\r\n label: { display: 'block', fontSize: 12, color: c.fg2, marginBottom: 4 },\r\n input: {\r\n width: '100%', boxSizing: 'border-box', padding: '5px 8px', fontSize: 12,\r\n border: `1px solid ${c.border}`, borderRadius: 6, background: c.bg, color: c.fg, outline: 'none',\r\n },\r\n select: {\r\n padding: '4px 6px', fontSize: 12, border: `1px solid ${c.border}`, borderRadius: 6,\r\n background: c.bg, color: c.fg, outline: 'none',\r\n },\r\n textarea: {\r\n width: '100%', boxSizing: 'border-box', padding: '5px 8px', fontSize: 12,\r\n border: `1px solid ${c.border}`, borderRadius: 6, background: c.bg, color: c.fg,\r\n outline: 'none', resize: 'vertical', minHeight: 54, fontFamily: 'inherit',\r\n },\r\n grid2: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 },\r\n grid3: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, alignItems: 'end' },\r\n head: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 },\r\n bar: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 8, flexWrap: 'wrap' },\r\n muted: { color: c.fg2 },\r\n link: { color: c.primary, cursor: 'pointer' },\r\n status: { fontWeight: 600 },\r\n tabs: {\r\n display: 'flex', gap: 18, marginBottom: 12, borderBottom: `1px solid ${c.border}`,\r\n },\r\n tab: {\r\n padding: '0 0 6px', fontSize: 13, fontWeight: 600, color: c.fg2,\r\n background: 'none', border: 'none', borderBottom: '2px solid transparent',\r\n cursor: 'pointer', transition: 'color 120ms ease, border-color 120ms ease',\r\n },\r\n activeTab: { color: c.fg, borderBottom: `2px solid ${c.primary}` },\r\n} as const\r\n\r\n/** 状态圆点配色(数据集)。 */\r\nexport const STATUS_COLOR: Record<string, string> = {\r\n ready: c.ok,\r\n importing: c.warn,\r\n failed: c.danger,\r\n}\r\n\r\n/** 数据源连通状态圆点配色。 */\r\nexport const SOURCE_STATUS_COLOR: Record<string, string> = {\r\n connected: c.ok,\r\n error: c.danger,\r\n unknown: c.fg2,\r\n}\r\n\r\nexport const SOURCE_STATUS_TEXT: Record<string, string> = {\r\n connected: '已连通',\r\n error: '连接失败',\r\n unknown: '未检测',\r\n}\r\n\r\n/** 类型徽标底色(低饱和,跟随主题的浅底细边框)。 */\r\nexport const TYPE_BADGE: Record<string, string> = {\r\n mysql: c.primary,\r\n postgresql: c.ok,\r\n}\r\n","/**\r\n * 跨工作区聚合的数据集列表表格。列:名称 / 工作区 / 行数 / 列数 / 状态 / 来源 / 更新时间。\r\n * 列表与详情/编辑是上下分区的:本组件只负责表格与分页,选中回调上抛给 Section。\r\n */\r\n\r\nimport type { ReactElement } from 'react'\r\nimport type { DatasetAdminView } from '../../admin-contract'\r\nimport { c, s, STATUS_COLOR } from './styles'\r\n\r\nconst STATUS_TEXT: Record<string, string> = { ready: '就绪', importing: '导入中', failed: '失败' }\r\n\r\nexport function fmtAgo(ts: number): string {\r\n if (!Number.isFinite(ts) || ts <= 0) return '—'\r\n const diff = Date.now() - ts\r\n if (diff < 60_000) return '刚刚'\r\n if (diff < 3_600_000) return `${Math.floor(diff / 60_000)} 分钟前`\r\n if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)} 小时前`\r\n return `${Math.floor(diff / 86_400_000)} 天前`\r\n}\r\n\r\nfunction ellipsis(value: string | null, max = 32): string {\r\n if (value === null) return '—'\r\n return value.length > max ? `${value.slice(0, max)}…` : value\r\n}\r\n\r\nexport interface DatasetTableProps {\r\n items: DatasetAdminView[]\r\n page: number\r\n pageSize: number\r\n total: number\r\n selectedId: string | null\r\n loading: boolean\r\n onSelect: (item: DatasetAdminView) => void\r\n onPageChange: (page: number) => void\r\n}\r\n\r\nexport function DatasetTable(props: DatasetTableProps): ReactElement {\r\n const { items, page, pageSize, total, selectedId, loading, onSelect, onPageChange } = props\r\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\r\n\r\n const rows = items.length === 0 && !loading\r\n ? (\r\n <tr>\r\n <td style={{ ...s.td, color: c.fg2, padding: '18px 8px', textAlign: 'center' }} colSpan={7}>\r\n 还没有数据集,用 agent 的 dataset_import 导入,或直接点击「新建数据集」\r\n </td>\r\n </tr>\r\n )\r\n : items.map(item => (\r\n <tr\r\n key={`${item.scopeKey}:${item.id}`}\r\n style={{ ...s.row, ...(item.id === selectedId ? s.selectedRow : null) }}\r\n onClick={() => onSelect(item)}\r\n >\r\n <td style={s.td} title={item.name}>{ellipsis(item.name, 40)}</td>\r\n <td style={s.td} title={item.scopeKey}>{ellipsis(item.scopeKey, 36)}</td>\r\n <td style={s.td}>{item.rowCount}</td>\r\n <td style={s.td}>{item.columnCount}</td>\r\n <td style={s.td}>\r\n <span style={{ ...s.dot, background: STATUS_COLOR[item.status] ?? c.fg2 }} />\r\n {STATUS_TEXT[item.status] ?? item.status}\r\n </td>\r\n <td style={s.td} title={item.sourcePath ?? ''}>{ellipsis(item.sourcePath, 40)}</td>\r\n <td style={{ ...s.td, color: c.fg2 }}>{fmtAgo(item.updatedAt)}</td>\r\n </tr>\r\n ))\r\n\r\n return (\r\n <div>\r\n <div style={s.scroll}>\r\n <table style={s.table}>\r\n <thead>\r\n <tr>\r\n <th style={s.th}>名称</th>\r\n <th style={s.th}>工作区</th>\r\n <th style={s.th}>行数</th>\r\n <th style={s.th}>列数</th>\r\n <th style={s.th}>状态</th>\r\n <th style={s.th}>来源</th>\r\n <th style={s.th}>更新时间</th>\r\n </tr>\r\n </thead>\r\n <tbody>{rows}</tbody>\r\n </table>\r\n </div>\r\n <div style={{ ...s.bar, display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(page <= 1 ? s.disabledButton : null) }}\r\n disabled={page <= 1}\r\n onClick={() => onPageChange(1)}\r\n >« 首页</button>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(page <= 1 ? s.disabledButton : null) }}\r\n disabled={page <= 1}\r\n onClick={() => onPageChange(page - 1)}\r\n >上一页</button>\r\n <span>第 {Math.min(page, totalPages)} / {totalPages} 页 · 共 {total} 个</span>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(page >= totalPages ? s.disabledButton : null) }}\r\n disabled={page >= totalPages}\r\n onClick={() => onPageChange(page + 1)}\r\n >下一页</button>\r\n {loading ? <span style={s.muted}>加载中…</span> : null}\r\n </div>\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 数据集详情与编辑面板。\r\n *\r\n * 可改:名称 / 描述 / 来源路径,以及列结构里的**说明与样例**(纯元数据)。\r\n * 不可改:列名 / 类型 / 可空性——它们对应物理表 DDL,由导入时推断确定。\r\n * 删除走二次确认。写操作受 `writable` 控制(只读模式或接口关闭时置灰)。\r\n */\r\n\r\nimport { useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport {\r\n ADMIN_COLUMN_TYPE_LABELS,\r\n ADMIN_MAX_SAMPLE_VALUES,\r\n type DatasetColumnPatch,\r\n type DatasetDetailView,\r\n type PatchDatasetRequest,\r\n} from '../../admin-contract'\r\nimport { patchDataset, deleteDataset } from './api'\r\nimport { c, s } from './styles'\r\n\r\n/** 样例分隔符:展示用全角顿号,输入同时接受半角与全角逗号。 */\r\nconst SAMPLE_SEPARATORS = /[、,,]/\r\n/** 空值的展示记号,与 `sampleToText` 互逆。 */\r\nconst NULL_MARK = '∅'\r\n\r\ninterface ColumnDraft {\r\n description: string\r\n sampleText: string\r\n}\r\n\r\nfunction sampleToText(sample: unknown[]): string {\r\n if (!Array.isArray(sample)) return ''\r\n return sample\r\n .slice(0, ADMIN_MAX_SAMPLE_VALUES)\r\n .map(value => (value === null ? NULL_MARK : String(value)))\r\n .join('、')\r\n}\r\n\r\nfunction textToSample(text: string): (string | null)[] {\r\n return text\r\n .split(SAMPLE_SEPARATORS)\r\n .map(value => value.trim())\r\n .filter(value => value.length > 0)\r\n .map(value => (value === NULL_MARK ? null : value))\r\n}\r\n\r\nfunction initialDrafts(columns: DatasetDetailView['columns']): Record<string, ColumnDraft> {\r\n const drafts: Record<string, ColumnDraft> = {}\r\n for (const column of columns) {\r\n drafts[column.sanitizedName] = {\r\n description: column.description ?? '',\r\n sampleText: sampleToText(column.sample),\r\n }\r\n }\r\n return drafts\r\n}\r\n\r\nexport interface DatasetEditorProps {\r\n detail: DatasetDetailView\r\n writable: boolean\r\n onChanged: (detail: DatasetDetailView) => void\r\n onDeleted: (id: string) => void\r\n onError: (error: Error) => void\r\n}\r\n\r\nexport function DatasetEditor(props: DatasetEditorProps): ReactElement {\r\n const { detail, writable, onChanged, onDeleted, onError } = props\r\n const [name, setName] = useState(detail.name)\r\n const [description, setDescription] = useState(detail.description ?? '')\r\n const [sourcePath, setSourcePath] = useState(detail.sourcePath ?? '')\r\n const [columnDrafts, setColumnDrafts] = useState<Record<string, ColumnDraft>>(() => initialDrafts(detail.columns))\r\n const [saving, setSaving] = useState(false)\r\n const [deleting, setDeleting] = useState(false)\r\n const [confirming, setConfirming] = useState(false)\r\n\r\n function updateColumnDraft(key: string, patch: Partial<ColumnDraft>): void {\r\n setColumnDrafts(previous => ({ ...previous, [key]: { ...previous[key], ...patch } }))\r\n }\r\n\r\n const columnDirty = detail.columns.some(column => {\r\n const draft = columnDrafts[column.sanitizedName]\r\n if (draft === undefined) return false\r\n return draft.description !== (column.description ?? '') || draft.sampleText !== sampleToText(column.sample)\r\n })\r\n const dirty =\r\n name !== detail.name ||\r\n description !== (detail.description ?? '') ||\r\n sourcePath !== (detail.sourcePath ?? '') ||\r\n columnDirty\r\n\r\n /** 只提交改动的列;未改动的列连同样例一并原样保留,不参与往返。 */\r\n function buildColumnPatches(): DatasetColumnPatch[] {\r\n const patches: DatasetColumnPatch[] = []\r\n for (const column of detail.columns) {\r\n const draft = columnDrafts[column.sanitizedName]\r\n if (draft === undefined) continue\r\n const patch: DatasetColumnPatch = { sanitizedName: column.sanitizedName }\r\n let changed = false\r\n if (draft.description !== (column.description ?? '')) {\r\n patch.description = draft.description\r\n changed = true\r\n }\r\n if (draft.sampleText !== sampleToText(column.sample)) {\r\n patch.sample = textToSample(draft.sampleText)\r\n changed = true\r\n }\r\n if (changed) patches.push(patch)\r\n }\r\n return patches\r\n }\r\n\r\n async function handleSave(): Promise<void> {\r\n if (!dirty || saving) return\r\n const columnPatches = buildColumnPatches()\r\n const body: PatchDatasetRequest = { name, description, sourcePath }\r\n if (columnPatches.length > 0) body.columns = columnPatches\r\n setSaving(true)\r\n try {\r\n const updated = await patchDataset(detail.id, detail.scopeKey, body)\r\n onChanged(updated)\r\n } catch (error) {\r\n onError(error instanceof Error ? error : new Error('保存失败'))\r\n } finally {\r\n setSaving(false)\r\n }\r\n }\r\n\r\n async function handleDelete(): Promise<void> {\r\n if (deleting) return\r\n setDeleting(true)\r\n try {\r\n await deleteDataset(detail.id, detail.scopeKey)\r\n onDeleted(detail.id)\r\n } catch (error) {\r\n onError(error instanceof Error ? error : new Error('删除失败'))\r\n setConfirming(false)\r\n } finally {\r\n setDeleting(false)\r\n }\r\n }\r\n\r\n const disabled = !writable || saving\r\n\r\n return (\r\n <div style={s.panel}>\r\n <div style={{ ...s.head, display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>\r\n <strong style={{ fontSize: 13 }}>数据集详情</strong>\r\n <span style={s.muted}>工作区:{detail.scopeKey}</span>\r\n </div>\r\n\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>名称</label>\r\n <input style={s.input} value={name} disabled={disabled} onChange={e => setName(e.target.value)} />\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>来源路径</label>\r\n <input style={s.input} value={sourcePath} disabled={disabled} onChange={e => setSourcePath(e.target.value)} />\r\n </div>\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>描述</label>\r\n <textarea style={s.textarea} value={description} disabled={disabled} onChange={e => setDescription(e.target.value)} />\r\n </div>\r\n\r\n <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.primaryButton, ...(disabled || !dirty ? s.disabledButton : null) }}\r\n disabled={disabled || !dirty}\r\n onClick={() => void handleSave()}\r\n >{saving ? '保存中…' : '保存修改'}</button>\r\n\r\n {!confirming\r\n ? (\r\n <button\r\n type=\"button\"\r\n style={{ ...s.dangerButton, ...(!writable ? s.disabledButton : null) }}\r\n disabled={!writable}\r\n onClick={() => setConfirming(true)}\r\n >删除数据集</button>\r\n )\r\n : (\r\n <>\r\n <span style={{ color: c.danger }}>确认删除「{detail.name}」及其全部数据?</span>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.dangerButton, ...(deleting ? s.disabledButton : null) }}\r\n disabled={deleting}\r\n onClick={() => void handleDelete()}\r\n >{deleting ? '删除中…' : '确认删除'}</button>\r\n <button type=\"button\" style={s.button} disabled={deleting} onClick={() => setConfirming(false)}>取消</button>\r\n </>\r\n )}\r\n {!writable ? <span style={s.muted}>(只读模式或接口已关闭,不可修改)</span> : null}\r\n </div>\r\n\r\n <div style={{ marginTop: 14 }}>\r\n <div style={{ ...s.label, marginBottom: 6 }}>\r\n 列结构 · 共 {detail.columns.length} 列 (列名 / 类型 / 可空性由导入时推断,不可修改;说明与样例可编辑)\r\n </div>\r\n <div style={{ ...s.scroll, maxHeight: 240 }}>\r\n <table style={s.table}>\r\n <thead>\r\n <tr>\r\n <th style={s.th}>列名</th>\r\n <th style={s.th}>类型</th>\r\n <th style={s.th}>可空</th>\r\n <th style={s.th}>说明</th>\r\n <th style={s.th}>样例</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {detail.columns.map(column => (\r\n <tr key={column.sanitizedName}>\r\n <td style={s.td} title={column.name}>{column.name}</td>\r\n <td style={s.td}>{ADMIN_COLUMN_TYPE_LABELS[column.type] ?? column.type}</td>\r\n <td style={s.td}>{column.nullable ? '是' : '否'}</td>\r\n <td style={{ ...s.td, overflow: 'visible' }}>\r\n <input\r\n style={s.input}\r\n value={columnDrafts[column.sanitizedName]?.description ?? ''}\r\n disabled={disabled}\r\n placeholder=\"补充说明,便于 agent 理解该列\"\r\n onChange={event => updateColumnDraft(column.sanitizedName, { description: event.target.value })}\r\n />\r\n </td>\r\n <td style={{ ...s.td, overflow: 'visible' }}>\r\n <input\r\n style={s.input}\r\n value={columnDrafts[column.sanitizedName]?.sampleText ?? ''}\r\n disabled={disabled}\r\n placeholder=\"多个值用 、 分隔\"\r\n onChange={event => updateColumnDraft(column.sanitizedName, { sampleText: event.target.value })}\r\n />\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 数据集的「表数据」分区:分页查看该数据集物理表里的数据行。\r\n *\r\n * 只读组件,不发任何写请求。取数走 `api.ts` 的 `listDatasetRows`,物理表与\r\n * SQL 都不出现在前端:只带 datasetId / scope 与分页参数。列顺序与列集合由\r\n * 服务端从元数据登记的业务列给出,`_row_id` 只当行键与行号展示。\r\n *\r\n * 翻页是「整页重取」而非增量累加:每页 20–100 行,页码由服务端按\r\n * `COUNT(*)` 夹紧,删除尾部行后停留在越界页码会自动落到末页。\r\n */\r\n\r\nimport { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport {\r\n ADMIN_ROW_ID_COLUMN,\r\n ADMIN_ROW_MAX_PAGE_SIZE,\r\n ADMIN_ROW_PAGE_SIZE,\r\n type DatasetRowsResult,\r\n} from '../../admin-contract'\r\nimport { listDatasetRows } from './api'\r\nimport { c, s } from './styles'\r\n\r\n/** 每页条数可选项:默认 20,最大取契约上限。 */\r\nconst PAGE_SIZE_OPTIONS = [...new Set([ADMIN_ROW_PAGE_SIZE, 50, ADMIN_ROW_MAX_PAGE_SIZE])].sort((a, b) => a - b)\r\n\r\n/** 单个单元格的展示字符上限(超长省略,完整值放 title)。 */\r\nconst CELL_MAX_LENGTH = 80\r\n\r\nfunction cellText(value: unknown): string {\r\n if (typeof value === 'string') return value\r\n if (typeof value === 'object' && value !== null) {\r\n try {\r\n return JSON.stringify(value)\r\n } catch {\r\n return '[object]'\r\n }\r\n }\r\n return String(value)\r\n}\r\n\r\nfunction Cell(props: { value: unknown }): ReactElement {\r\n const { value } = props\r\n if (value === null || value === undefined) {\r\n return <span style={{ color: c.fg2 }}>—</span>\r\n }\r\n const text = cellText(value)\r\n return <span title={text}>{text.length > CELL_MAX_LENGTH ? `${text.slice(0, CELL_MAX_LENGTH)}…` : text}</span>\r\n}\r\n\r\nexport interface RowsPanelProps {\r\n datasetId: string\r\n scopeKey: string\r\n /** 元数据登记的行数,只用于与真实行数比对后给出提示。 */\r\n recordedRows: number\r\n /** 数据集状态:非就绪时给出提示,但不阻断查看。 */\r\n status: string\r\n}\r\n\r\nexport function RowsPanel(props: RowsPanelProps): ReactElement {\r\n const { datasetId, scopeKey, recordedRows, status } = props\r\n const [data, setData] = useState<DatasetRowsResult | null>(null)\r\n const [page, setPage] = useState(1)\r\n const [pageSize, setPageSize] = useState(ADMIN_ROW_PAGE_SIZE)\r\n const [loading, setLoading] = useState(false)\r\n const [error, setError] = useState<string | null>(null)\r\n const requestId = useRef(0)\r\n\r\n const load = useCallback(async (target: number, size: number): Promise<void> => {\r\n const id = requestId.current + 1\r\n requestId.current = id\r\n setLoading(true)\r\n setError(null)\r\n try {\r\n const result = await listDatasetRows(datasetId, scopeKey, target, size)\r\n if (id !== requestId.current) return\r\n setData(result)\r\n // 服务端会把页码夹到分页边界内,以它返回的为准。\r\n setPage(result.page)\r\n } catch (failure) {\r\n if (id !== requestId.current) return\r\n setError(failure instanceof Error ? failure.message : '加载数据失败')\r\n } finally {\r\n if (id === requestId.current) setLoading(false)\r\n }\r\n }, [datasetId, scopeKey])\r\n\r\n // 换数据集(外层用 key 重挂载)或改每页条数:回到第一页重取。\r\n useEffect(() => {\r\n setData(null)\r\n void load(1, pageSize)\r\n }, [load, pageSize])\r\n\r\n const total = data?.total ?? 0\r\n const totalPages = data?.totalPages ?? 1\r\n const columns = data?.columns ?? []\r\n const rows = data?.rows ?? []\r\n\r\n return (\r\n <div style={s.panel}>\r\n <div style={{ ...s.head, display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>\r\n <strong style={{ fontSize: 13 }}>表数据</strong>\r\n <span style={s.muted}>{loading ? '加载中…' : `共 ${total} 行`}</span>\r\n </div>\r\n\r\n {status !== 'ready' ? <div style={{ ...s.muted, marginBottom: 8 }}>数据集当前状态为 {status},数据可能不完整。</div> : null}\r\n {error !== null ? <div style={{ ...s.banner, marginTop: 0 }}><span>{error}</span></div> : null}\r\n {recordedRows !== total && error === null && data !== null\r\n ? <div style={{ ...s.muted, marginBottom: 8 }}>元数据登记 {recordedRows} 行,实际 {total} 行。</div>\r\n : null}\r\n\r\n {columns.length > 0\r\n ? (\r\n <div style={{ ...s.scroll, maxHeight: 320 }}>\r\n <table style={s.table}>\r\n <thead>\r\n <tr>\r\n <th style={s.th}>#</th>\r\n {columns.map(column => <th key={column} style={s.th} title={column}>{column}</th>)}\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {rows.map(row => (\r\n <tr key={String(row[ADMIN_ROW_ID_COLUMN] ?? '')}>\r\n <td style={{ ...s.td, color: c.fg2, maxWidth: 60 }}>{String(row[ADMIN_ROW_ID_COLUMN] ?? '')}</td>\r\n {columns.map(column => (\r\n <td key={column} style={s.td}><Cell value={row[column]} /></td>\r\n ))}\r\n </tr>\r\n ))}\r\n {rows.length === 0 && !loading\r\n ? (\r\n <tr>\r\n <td style={{ ...s.td, color: c.fg2, padding: '16px 8px', textAlign: 'center' }} colSpan={columns.length + 1}>\r\n {total === 0 ? '该数据集还没有数据行' : '这一页没有数据'}\r\n </td>\r\n </tr>\r\n )\r\n : null}\r\n </tbody>\r\n </table>\r\n </div>\r\n )\r\n : loading\r\n ? <div style={s.muted}>加载中…</div>\r\n : error === null\r\n ? <div style={s.muted}>该数据集没有已登记的列,无法展示数据。</div>\r\n : null}\r\n\r\n <div style={{ ...s.bar, display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(page <= 1 || loading ? s.disabledButton : null) }}\r\n disabled={page <= 1 || loading}\r\n onClick={() => void load(1, pageSize)}\r\n >« 首页</button>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(page <= 1 || loading ? s.disabledButton : null) }}\r\n disabled={page <= 1 || loading}\r\n onClick={() => void load(page - 1, pageSize)}\r\n >上一页</button>\r\n <span>第 {page} / {totalPages} 页</span>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(page >= totalPages || loading ? s.disabledButton : null) }}\r\n disabled={page >= totalPages || loading}\r\n onClick={() => void load(page + 1, pageSize)}\r\n >下一页</button>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(page >= totalPages || loading ? s.disabledButton : null) }}\r\n disabled={page >= totalPages || loading}\r\n onClick={() => void load(totalPages, pageSize)}\r\n >末页 »</button>\r\n <label style={{ ...s.muted, display: 'flex', alignItems: 'center', gap: 4 }}>\r\n 每页\r\n <select\r\n style={s.select}\r\n value={pageSize}\r\n disabled={loading}\r\n onChange={event => setPageSize(Number(event.target.value))}\r\n >\r\n {PAGE_SIZE_OPTIONS.map(size => <option key={size} value={size}>{size}</option>)}\r\n </select>\r\n 行\r\n </label>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(loading ? s.disabledButton : null) }}\r\n disabled={loading}\r\n onClick={() => void load(page, pageSize)}\r\n >刷新</button>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 新建空数据集表单:选工作区 + 填名称/描述/来源 + 逐行定义列。\r\n * 列结构提交后即冻结(设置页不支持改列)。提交走 `createDataset`。\r\n */\r\n\r\nimport { useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport {\r\n ADMIN_COLUMN_TYPES,\r\n ADMIN_COLUMN_TYPE_LABELS,\r\n ADMIN_MAX_COLUMNS,\r\n type DatasetColumnSpec,\r\n type ScopeView,\r\n} from '../../admin-contract'\r\nimport { createDataset } from './api'\r\nimport { c, s } from './styles'\r\n\r\nexport interface CreateFormProps {\r\n scopes: ScopeView[]\r\n onCreated: (detail: unknown) => void\r\n onCancel: () => void\r\n onError: (error: Error) => void\r\n}\r\n\r\ninterface ColumnDraft {\r\n name: string\r\n type: string\r\n nullable: boolean\r\n description: string\r\n}\r\n\r\nconst EMPTY_COLUMN: ColumnDraft = { name: '', type: 'text', nullable: false, description: '' }\r\n\r\nexport function CreateForm(props: CreateFormProps): ReactElement {\r\n const { scopes, onCreated, onCancel, onError } = props\r\n const [scopeKey, setScopeKey] = useState(scopes[0]?.scopeKey ?? '')\r\n const [name, setName] = useState('')\r\n const [description, setDescription] = useState('')\r\n const [sourcePath, setSourcePath] = useState('')\r\n const [columns, setColumns] = useState<ColumnDraft[]>([{ ...EMPTY_COLUMN }])\r\n const [submitting, setSubmitting] = useState(false)\r\n\r\n const canSubmit =\r\n scopeKey.length > 0 &&\r\n name.trim().length > 0 &&\r\n columns.length > 0 &&\r\n columns.length <= ADMIN_MAX_COLUMNS &&\r\n columns.every(column => column.name.trim().length > 0)\r\n\r\n function updateColumn(index: number, patch: Partial<ColumnDraft>): void {\r\n setColumns(previous => previous.map((column, i) => (i === index ? { ...column, ...patch } : column)))\r\n }\r\n\r\n async function handleSubmit(): Promise<void> {\r\n if (!canSubmit || submitting) return\r\n const body = {\r\n scopeKey,\r\n name: name.trim(),\r\n description: description.trim().length > 0 ? description.trim() : undefined,\r\n sourcePath: sourcePath.trim().length > 0 ? sourcePath.trim() : null,\r\n columns: columns.map<{ name: string; type: DatasetColumnSpec['type']; nullable: boolean; description?: string }>(column => ({\r\n name: column.name.trim(),\r\n type: column.type as DatasetColumnSpec['type'],\r\n nullable: column.nullable,\r\n description: column.description.trim().length > 0 ? column.description.trim() : undefined,\r\n })),\r\n }\r\n setSubmitting(true)\r\n try {\r\n const created = await createDataset(body)\r\n onCreated(created)\r\n } catch (error) {\r\n onError(error instanceof Error ? error : new Error('新建失败'))\r\n } finally {\r\n setSubmitting(false)\r\n }\r\n }\r\n\r\n return (\r\n <div style={s.panel}>\r\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>\r\n <strong style={{ fontSize: 13 }}>新建数据集</strong>\r\n <span style={s.muted}>空表,建好后用 agent 工具灌入数据</span>\r\n </div>\r\n\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>工作区</label>\r\n <select style={s.input} value={scopeKey} disabled={submitting} onChange={e => setScopeKey(e.target.value)}>\r\n {scopes.length === 0\r\n ? <option value=\"\">(暂无可用工作区)</option>\r\n : scopes.map(scope => <option key={scope.scopeKey} value={scope.scopeKey}>{scope.scopeKey}</option>)}\r\n </select>\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>名称</label>\r\n <input style={s.input} value={name} disabled={submitting} onChange={e => setName(e.target.value)} />\r\n </div>\r\n </div>\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>描述</label>\r\n <input style={s.input} value={description} disabled={submitting} onChange={e => setDescription(e.target.value)} />\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>来源路径(可选)</label>\r\n <input style={s.input} value={sourcePath} disabled={submitting} onChange={e => setSourcePath(e.target.value)} />\r\n </div>\r\n </div>\r\n\r\n <div style={s.label}>列定义({columns.length} / {ADMIN_MAX_COLUMNS})</div>\r\n <div style={{ ...s.scroll, maxHeight: 220, marginBottom: 10 }}>\r\n <table style={s.table}>\r\n <thead>\r\n <tr>\r\n <th style={s.th}>列名</th>\r\n <th style={s.th}>类型</th>\r\n <th style={s.th}>可空</th>\r\n <th style={s.th}>说明</th>\r\n <th style={s.th} />\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {columns.map((column, index) => (\r\n <tr key={index}>\r\n <td style={s.td}>\r\n <input\r\n style={{ ...s.input, border: column.name.trim().length === 0 ? `1px solid ${c.danger}` : s.input.border }}\r\n value={column.name}\r\n disabled={submitting}\r\n onChange={e => updateColumn(index, { name: e.target.value })}\r\n />\r\n </td>\r\n <td style={s.td}>\r\n <select\r\n style={s.input}\r\n value={column.type}\r\n disabled={submitting}\r\n onChange={e => updateColumn(index, { type: e.target.value })}\r\n >\r\n {ADMIN_COLUMN_TYPES.map(type => (\r\n <option key={type} value={type}>{ADMIN_COLUMN_TYPE_LABELS[type]}</option>\r\n ))}\r\n </select>\r\n </td>\r\n <td style={s.td}>\r\n <input\r\n type=\"checkbox\"\r\n checked={column.nullable}\r\n disabled={submitting}\r\n onChange={e => updateColumn(index, { nullable: e.target.checked })}\r\n />\r\n </td>\r\n <td style={s.td}>\r\n <input style={s.input} value={column.description} disabled={submitting} onChange={e => updateColumn(index, { description: e.target.value })} />\r\n </td>\r\n <td style={s.td}>\r\n <span\r\n style={{ ...s.link, ...(columns.length <= 1 || submitting ? s.disabledButton : null) }}\r\n onClick={() => { if (columns.length > 1 && !submitting) setColumns(previous => previous.filter((_, i) => i !== index)) }}\r\n >删除</span>\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(columns.length >= ADMIN_MAX_COLUMNS || submitting ? s.disabledButton : null) }}\r\n disabled={columns.length >= ADMIN_MAX_COLUMNS || submitting}\r\n onClick={() => setColumns(previous => [...previous, { ...EMPTY_COLUMN }])}\r\n >添加一列</button>\r\n\r\n <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.primaryButton, ...(!canSubmit || submitting ? s.disabledButton : null) }}\r\n disabled={!canSubmit || submitting}\r\n onClick={() => void handleSubmit()}\r\n >{submitting ? '提交中…' : '提交'}</button>\r\n <button type=\"button\" style={s.button} disabled={submitting} onClick={onCancel}>取消</button>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 「数据集」页签:顶部工具条(搜索 / 新建 / 刷新)+ 聚合列表表格 + 详情/编辑或新建面板 +\r\n * 表数据分页面板。所有数据来自 `api.ts`,跨工作区聚合与表数据读取由主机侧完成。\r\n *\r\n * 状态以「列表 / 详情 / 新建」分区呈现:表格常驻,选中行在下方展开详情与\r\n * 只读的表数据分页,「新建」时详情区替换为新建表单(此时没有表数据可看)。\r\n * 写操作的只读/关闭判定由错误码驱动。\r\n */\r\n\r\nimport { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport {\r\n ADMIN_PAGE_SIZE,\r\n type DatasetAdminView,\r\n type DatasetDetailView,\r\n type ScopeView,\r\n} from '../../admin-contract'\r\nimport { AdminApiError, getDataset, listDatasets, listScopes } from './api'\r\nimport { DatasetTable } from './DatasetTable'\r\nimport { DatasetEditor } from './DatasetEditor'\r\nimport { RowsPanel } from './RowsPanel'\r\nimport { CreateForm } from './CreateForm'\r\nimport { s } from './styles'\r\n\r\ntype Mode = 'browse' | 'create'\r\n\r\nexport function DatasetsPanel(): ReactElement {\r\n const [scopes, setScopes] = useState<ScopeView[]>([])\r\n const [items, setItems] = useState<DatasetAdminView[]>([])\r\n const [total, setTotal] = useState(0)\r\n const [query, setQuery] = useState('')\r\n const [page, setPage] = useState(1)\r\n const [mode, setMode] = useState<Mode>('browse')\r\n const [selectedId, setSelectedId] = useState<string | null>(null)\r\n const [detail, setDetail] = useState<DatasetDetailView | null>(null)\r\n const [loadingList, setLoadingList] = useState(false)\r\n const [loadingDetail, setLoadingDetail] = useState(false)\r\n const [writable, setWritable] = useState(true)\r\n const [error, setError] = useState<string | null>(null)\r\n const requestId = useRef(0)\r\n\r\n const loadScopes = useCallback(async (): Promise<void> => {\r\n try {\r\n const result = await listScopes()\r\n setScopes(result.scopes)\r\n } catch (failure) {\r\n // 路由未挂载等同无可用工作区;列表加载会给出更具体的错误。\r\n if (failure instanceof AdminApiError && (failure.code === 'READ_ONLY' || failure.code === 'ADMIN_DISABLED')) {\r\n setWritable(false)\r\n }\r\n }\r\n }, [])\r\n\r\n const loadList = useCallback(async (q: string, p: number): Promise<void> => {\r\n const id = requestId.current + 1\r\n requestId.current = id\r\n setLoadingList(true)\r\n try {\r\n const result = await listDatasets(q, p, ADMIN_PAGE_SIZE)\r\n if (id !== requestId.current) return\r\n setItems(result.items)\r\n setTotal(result.total)\r\n const warning = result.warnings.map(w => `${w.scopeKey}: ${w.message}`).join(';')\r\n setError(warning.length > 0 ? warning : null)\r\n } catch (failure) {\r\n if (id !== requestId.current) return\r\n setError(failure instanceof Error ? failure.message : '加载失败')\r\n } finally {\r\n if (id === requestId.current) setLoadingList(false)\r\n }\r\n }, [])\r\n\r\n // 初次挂载:工作区列表 + 首屏列表。\r\n useEffect(() => {\r\n void loadScopes()\r\n void loadList('', 1)\r\n }, [loadScopes, loadList])\r\n\r\n // 关键字防抖:停止输入 300ms 后回到第一页重新拉取。\r\n useEffect(() => {\r\n const timer = setTimeout(() => {\r\n setPage(1)\r\n void loadList(query, 1)\r\n }, 300)\r\n return () => clearTimeout(timer)\r\n }, [query, loadList])\r\n\r\n const openDetail = useCallback(async (item: DatasetAdminView): Promise<void> => {\r\n setSelectedId(item.id)\r\n setMode('browse')\r\n setDetail(null)\r\n setLoadingDetail(true)\r\n try {\r\n const fetched = await getDataset(item.id, item.scopeKey)\r\n setDetail(fetched)\r\n } catch (failure) {\r\n setError(failure instanceof Error ? failure.message : '加载详情失败')\r\n } finally {\r\n setLoadingDetail(false)\r\n }\r\n }, [])\r\n\r\n const handleWriteError = useCallback((failure: Error): void => {\r\n setError(failure.message)\r\n if (failure instanceof AdminApiError && (failure.code === 'READ_ONLY' || failure.code === 'ADMIN_DISABLED')) {\r\n setWritable(false)\r\n }\r\n }, [])\r\n\r\n const handleChanged = useCallback((updated: DatasetDetailView): void => {\r\n setDetail(updated)\r\n void loadList(query, page)\r\n }, [query, page, loadList])\r\n\r\n const handleDeleted = useCallback(async (id: string): Promise<void> => {\r\n setSelectedId(null)\r\n setDetail(null)\r\n await loadScopes()\r\n await loadList(query, page)\r\n void id\r\n }, [query, page, loadList, loadScopes])\r\n\r\n const handleCreated = useCallback(async (created: unknown): Promise<void> => {\r\n setMode('browse')\r\n await loadScopes()\r\n await loadList(query, page)\r\n const record = created as DatasetDetailView\r\n setSelectedId(record.id)\r\n setDetail(record)\r\n }, [query, page, loadList, loadScopes])\r\n\r\n return (\r\n <div>\r\n <div style={s.toolbar}>\r\n <input\r\n style={s.search}\r\n placeholder=\"按名称 / 来源 / 工作区搜索…\"\r\n value={query}\r\n onChange={e => setQuery(e.target.value)}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{ ...s.primaryButton, ...(!writable ? s.disabledButton : null) }}\r\n disabled={!writable}\r\n onClick={() => setMode('create')}\r\n title={writable ? '新建空数据集' : '只读模式或接口已关闭'}\r\n >+ 新建数据集</button>\r\n <button type=\"button\" style={s.button} disabled={loadingList} onClick={() => void loadList(query, page)}>刷新</button>\r\n </div>\r\n\r\n {error !== null\r\n ? (\r\n <div style={s.banner}>\r\n <span>{error}</span>\r\n <span style={{ ...s.link, marginLeft: 'auto' }} onClick={() => setError(null)}>忽略</span>\r\n </div>\r\n )\r\n : null}\r\n\r\n <DatasetTable\r\n items={items}\r\n page={page}\r\n pageSize={ADMIN_PAGE_SIZE}\r\n total={total}\r\n selectedId={selectedId}\r\n loading={loadingList}\r\n onSelect={item => void openDetail(item)}\r\n onPageChange={p => { setPage(p); void loadList(query, p) }}\r\n />\r\n\r\n {mode === 'create'\r\n ? (\r\n <CreateForm\r\n scopes={scopes}\r\n onCreated={detail => void handleCreated(detail)}\r\n onCancel={() => setMode('browse')}\r\n onError={handleWriteError}\r\n />\r\n )\r\n : detail !== null\r\n ? (\r\n <>\r\n <DatasetEditor\r\n detail={detail}\r\n writable={writable}\r\n onChanged={handleChanged}\r\n onDeleted={id => void handleDeleted(id)}\r\n onError={handleWriteError}\r\n />\r\n {/* key 让切换数据集时重置分页状态,避免残留上一张表的页码。 */}\r\n <RowsPanel\r\n key={`${detail.scopeKey}:${detail.id}`}\r\n datasetId={detail.id}\r\n scopeKey={detail.scopeKey}\r\n recordedRows={detail.rowCount}\r\n status={detail.status}\r\n />\r\n </>\r\n )\r\n : (\r\n <div style={{ ...s.muted, marginTop: 12 }}>\r\n {loadingDetail ? '加载详情中…' : '选择一个数据集查看详情,或点击「新建数据集」创建空表。'}\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 数据源新建 / 编辑表单。\r\n *\r\n * 两步提交:**先测连、后保存**。连接参数(类型 / 主机 / 端口 / 库 / 用户 / 密码)任一改动\r\n * 都会把测试结果打回「未测试」,未测通时保存按钮禁用;服务端在保存时也会再测一次,\r\n * 避免前端被绕过。编辑态下密码留空表示「保留原密码」。\r\n */\r\n\r\nimport { useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport {\r\n ADMIN_SOURCE_DEFAULT_PORTS,\r\n ADMIN_SOURCE_TYPES,\r\n ADMIN_SOURCE_TYPE_LABELS,\r\n type ConnectionTestView,\r\n type DataSourceView,\r\n type TestDataSourceRequest,\r\n} from '../../admin-contract'\r\nimport { createDataSource, patchDataSource, testDataSource } from './api'\r\nimport { c, s } from './styles'\r\n\r\nexport interface DataSourceFormProps {\r\n /** 传入即编辑态;null / undefined 为新建。 */\r\n initial?: DataSourceView | null\r\n onSaved: (view: DataSourceView) => void\r\n onCancel: () => void\r\n onError: (error: Error) => void\r\n}\r\n\r\ntype TestState = 'idle' | 'testing' | 'ok' | 'fail'\r\n\r\n/** 连接参数的具体形态:测连、新建、编辑三处共用同一份草稿。 */\r\ninterface ConnectionFields {\r\n type: string\r\n host: string\r\n port: number\r\n database: string\r\n username: string\r\n password: string\r\n sslMode: string | null\r\n poolMax: number | null\r\n}\r\n\r\nexport function DataSourceForm(props: DataSourceFormProps): ReactElement {\r\n const { initial, onSaved, onCancel, onError } = props\r\n const editing = initial !== null && initial !== undefined\r\n const defaultType = initial?.type ?? 'mysql'\r\n\r\n const [name, setName] = useState(initial?.name ?? '')\r\n const [type, setType] = useState(defaultType)\r\n const [host, setHost] = useState(initial?.host ?? '127.0.0.1')\r\n const [port, setPort] = useState(String(initial?.port ?? ADMIN_SOURCE_DEFAULT_PORTS[defaultType]))\r\n const [database, setDatabase] = useState(initial?.database ?? '')\r\n const [username, setUsername] = useState(initial?.username ?? '')\r\n const [password, setPassword] = useState('')\r\n const [sslMode, setSslMode] = useState(initial?.sslMode ?? '')\r\n const [poolMax, setPoolMax] = useState(initial?.poolMax === null ? '' : String(initial?.poolMax ?? ''))\r\n const [description, setDescription] = useState(initial?.description ?? '')\r\n const [testState, setTestState] = useState<TestState>('idle')\r\n const [testResult, setTestResult] = useState<ConnectionTestView | null>(null)\r\n const [saving, setSaving] = useState(false)\r\n\r\n const requiredFilled =\r\n name.trim().length > 0 && host.trim().length > 0 && database.trim().length > 0 && username.trim().length > 0\r\n\r\n /** 连接参数变了,之前的测试结论就作废。 */\r\n function changeConnection(next: () => void): void {\r\n next()\r\n setTestState('idle')\r\n setTestResult(null)\r\n }\r\n\r\n function draft(): ConnectionFields {\r\n const parsedPort = Number.parseInt(port, 10)\r\n const parsedPool = Number.parseInt(poolMax, 10)\r\n return {\r\n type,\r\n host: host.trim(),\r\n port: Number.isFinite(parsedPort) ? parsedPort : ADMIN_SOURCE_DEFAULT_PORTS[type],\r\n database: database.trim(),\r\n username: username.trim(),\r\n password,\r\n sslMode: sslMode.trim().length > 0 ? sslMode.trim() : null,\r\n poolMax: Number.isFinite(parsedPool) ? parsedPool : null,\r\n }\r\n }\r\n\r\n async function handleTest(): Promise<void> {\r\n if (!requiredFilled || testState === 'testing') return\r\n setTestState('testing')\r\n try {\r\n const result = await testDataSource(draft())\r\n setTestResult(result)\r\n setTestState(result.success ? 'ok' : 'fail')\r\n } catch (error) {\r\n setTestState('fail')\r\n onError(error instanceof Error ? error : new Error('测试连接失败'))\r\n }\r\n }\r\n\r\n async function handleSave(): Promise<void> {\r\n if (testState !== 'ok' || saving) return\r\n setSaving(true)\r\n try {\r\n const connection = draft()\r\n const saved = editing\r\n ? await patchDataSource(initial.id, {\r\n name: name.trim(),\r\n ...connection,\r\n // 密码留空 = 保留原密码;填了才替换。\r\n ...(password.length > 0 ? {} : { password: undefined }),\r\n description: description.trim().length > 0 ? description.trim() : null,\r\n test: true,\r\n })\r\n : await createDataSource({\r\n ...connection,\r\n name: name.trim(),\r\n description: description.trim().length > 0 ? description.trim() : null,\r\n test: true,\r\n })\r\n onSaved(saved)\r\n } catch (error) {\r\n onError(error instanceof Error ? error : new Error('保存失败'))\r\n } finally {\r\n setSaving(false)\r\n }\r\n }\r\n\r\n const testHint = testState === 'ok'\r\n ? `连接成功(${testResult?.latency ?? 0}ms${testResult?.version === null ? '' : ` · ${testResult?.version}`})`\r\n : testState === 'fail'\r\n ? (testResult?.error ?? '连接失败')\r\n : '保存前请先测试连接'\r\n\r\n return (\r\n <div style={s.panel}>\r\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>\r\n <strong style={{ fontSize: 13 }}>{editing ? '编辑数据源' : '新建数据源'}</strong>\r\n <span style={s.muted}>\r\n {editing && initial.hasPassword ? '已保存过密码,留空即保留' : '密码加密存储,不会回显'}\r\n </span>\r\n </div>\r\n\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>名称</label>\r\n <input style={s.input} value={name} disabled={saving} onChange={e => setName(e.target.value)} />\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>类型</label>\r\n <select\r\n style={s.input}\r\n value={type}\r\n disabled={saving}\r\n onChange={e => changeConnection(() => {\r\n setType(e.target.value)\r\n setPort(String(ADMIN_SOURCE_DEFAULT_PORTS[e.target.value]))\r\n })}\r\n >\r\n {ADMIN_SOURCE_TYPES.map(item => (\r\n <option key={item} value={item}>{ADMIN_SOURCE_TYPE_LABELS[item]}</option>\r\n ))}\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>主机</label>\r\n <input\r\n style={s.input}\r\n value={host}\r\n disabled={saving}\r\n onChange={e => changeConnection(() => setHost(e.target.value))}\r\n />\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>端口</label>\r\n <input\r\n style={s.input}\r\n value={port}\r\n inputMode=\"numeric\"\r\n disabled={saving}\r\n onChange={e => changeConnection(() => setPort(e.target.value))}\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>数据库名</label>\r\n <input\r\n style={s.input}\r\n value={database}\r\n disabled={saving}\r\n onChange={e => changeConnection(() => setDatabase(e.target.value))}\r\n />\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>用户名</label>\r\n <input\r\n style={s.input}\r\n value={username}\r\n disabled={saving}\r\n onChange={e => changeConnection(() => setUsername(e.target.value))}\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>{editing ? '重设密码(留空保留)' : '密码'}</label>\r\n <input\r\n style={s.input}\r\n type=\"password\"\r\n value={password}\r\n disabled={saving}\r\n onChange={e => changeConnection(() => setPassword(e.target.value))}\r\n />\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>SSL 模式(可选)</label>\r\n <input\r\n style={s.input}\r\n value={sslMode}\r\n disabled={saving}\r\n placeholder={type === 'mysql' ? 'disabled / preferred / required' : 'disable / require / verify-full'}\r\n onChange={e => changeConnection(() => setSslMode(e.target.value))}\r\n />\r\n </div>\r\n </div>\r\n\r\n <div style={s.grid2}>\r\n <div style={s.field}>\r\n <label style={s.label}>连接池上限(可选)</label>\r\n <input\r\n style={s.input}\r\n value={poolMax}\r\n inputMode=\"numeric\"\r\n disabled={saving}\r\n onChange={e => changeConnection(() => setPoolMax(e.target.value))}\r\n />\r\n </div>\r\n <div style={s.field}>\r\n <label style={s.label}>描述(可选)</label>\r\n <input style={s.input} value={description} disabled={saving} onChange={e => setDescription(e.target.value)} />\r\n </div>\r\n </div>\r\n\r\n <div\r\n style={{\r\n ...s.banner,\r\n marginBottom: 10,\r\n borderColor: testState === 'ok' ? c.ok : testState === 'fail' ? c.danger : c.border,\r\n color: testState === 'ok' ? c.ok : testState === 'fail' ? c.danger : c.fg2,\r\n }}\r\n >\r\n <span>{testHint}</span>\r\n </div>\r\n\r\n <div style={{ display: 'flex', gap: 8 }}>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.button, ...(!requiredFilled || testState === 'testing' ? s.disabledButton : null) }}\r\n disabled={!requiredFilled || testState === 'testing'}\r\n onClick={() => void handleTest()}\r\n >{testState === 'testing' ? '测试中…' : '测试连接'}</button>\r\n <button\r\n type=\"button\"\r\n style={{ ...s.primaryButton, ...(testState !== 'ok' || saving ? s.disabledButton : null) }}\r\n disabled={testState !== 'ok' || saving}\r\n onClick={() => void handleSave()}\r\n >{saving ? '保存中…' : '保存'}</button>\r\n <button type=\"button\" style={s.button} disabled={saving} onClick={onCancel}>取消</button>\r\n </div>\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 远端表浏览与导入:选 schema(PostgreSQL)→ 过滤 / 点选一张表 → 选目标工作区并导入。\r\n *\r\n * 只读浏览(GET /sources/:id/tables),导入是写操作(POST /sources/:id/import)。\r\n * 导入完成后产出的是普通数据集,之后一律回到「数据集」页签用既有能力操作。\r\n */\r\n\r\nimport { useCallback, useEffect, useRef, useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport {\r\n ADMIN_COLUMN_TYPE_LABELS,\r\n type DataSourceView,\r\n type ImportSourceTableResult,\r\n type ScopeView,\r\n type SourceTableView,\r\n} from '../../admin-contract'\r\nimport { importSourceTable, listSourceTables } from './api'\r\nimport { c, s } from './styles'\r\n\r\nexport interface SourceTablesPanelProps {\r\n source: DataSourceView\r\n scopes: ScopeView[]\r\n onError: (error: Error) => void\r\n}\r\n\r\nfunction formatRows(count: number): string {\r\n if (count < 0) return '未知'\r\n if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M`\r\n if (count >= 1_000) return `${(count / 1_000).toFixed(1)}k`\r\n return String(count)\r\n}\r\n\r\nexport function SourceTablesPanel(props: SourceTablesPanelProps): ReactElement {\r\n const { source, scopes, onError } = props\r\n const [schemas, setSchemas] = useState<string[]>([])\r\n const [schema, setSchema] = useState('')\r\n const [keyword, setKeyword] = useState('')\r\n const [tables, setTables] = useState<SourceTableView[]>([])\r\n const [loading, setLoading] = useState(false)\r\n const [selected, setSelected] = useState<string | null>(null)\r\n const [scopeKey, setScopeKey] = useState('')\r\n const [name, setName] = useState('')\r\n const [importing, setImporting] = useState(false)\r\n const [result, setResult] = useState<ImportSourceTableResult | null>(null)\r\n const requestId = useRef(0)\r\n\r\n const load = useCallback(async (schemaArg: string, q: string): Promise<void> => {\r\n const id = requestId.current + 1\r\n requestId.current = id\r\n setLoading(true)\r\n try {\r\n const loaded = await listSourceTables(source.id, schemaArg, q)\r\n if (id !== requestId.current) return\r\n setSchemas(loaded.schemas)\r\n setTables(loaded.tables)\r\n setSelected(null)\r\n } catch (failure) {\r\n if (id !== requestId.current) return\r\n onError(failure instanceof Error ? failure : new Error('加载远端表失败'))\r\n } finally {\r\n if (id === requestId.current) setLoading(false)\r\n }\r\n }, [source.id, onError])\r\n\r\n // 切换数据源或 schema 立即重载;关键字走 300ms 防抖。\r\n useEffect(() => {\r\n const timer = setTimeout(() => void load(schema, keyword), keyword.trim().length > 0 ? 300 : 0)\r\n return () => clearTimeout(timer)\r\n }, [load, schema, keyword])\r\n\r\n useEffect(() => {\r\n if (scopeKey.length === 0 && scopes.length > 0) setScopeKey(scopes[0].scopeKey)\r\n }, [scopes, scopeKey])\r\n\r\n const current = tables.find(table => table.tableName === selected) ?? null\r\n\r\n async function handleImport(): Promise<void> {\r\n if (selected === null || scopeKey.length === 0 || importing) return\r\n setImporting(true)\r\n setResult(null)\r\n try {\r\n const imported = await importSourceTable(source.id, {\r\n scopeKey,\r\n tableName: selected,\r\n schemaName: schema.trim().length > 0 ? schema.trim() : null,\r\n name: name.trim().length > 0 ? name.trim() : null,\r\n })\r\n setResult(imported)\r\n } catch (failure) {\r\n onError(failure instanceof Error ? failure : new Error('导入失败'))\r\n } finally {\r\n setImporting(false)\r\n }\r\n }\r\n\r\n return (\r\n <div style={{ ...s.panel, background: 'transparent', borderTop: `1px solid ${c.border}`, borderRadius: 0 }}>\r\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>\r\n <strong style={{ fontSize: 13 }}>远端表</strong>\r\n <span style={s.muted}>{loading ? '加载中…' : `共 ${tables.length} 张`}</span>\r\n </div>\r\n\r\n <div style={{ display: 'flex', gap: 8, marginBottom: 10, flexWrap: 'wrap' }}>\r\n <select\r\n style={s.select}\r\n value={schema}\r\n disabled={loading || schemas.length <= 1}\r\n onChange={e => setSchema(e.target.value)}\r\n >\r\n {schemas.length === 0\r\n ? <option value=\"\">(默认)</option>\r\n : schemas.map(item => <option key={item} value={item}>{item}</option>)}\r\n </select>\r\n <input\r\n style={{ ...s.search, flex: 1, minWidth: 140 }}\r\n placeholder=\"按表名过滤…\"\r\n value={keyword}\r\n onChange={e => setKeyword(e.target.value)}\r\n />\r\n <button type=\"button\" style={s.button} disabled={loading} onClick={() => void load(schema, keyword)}>刷新</button>\r\n </div>\r\n\r\n <div style={{ ...s.scroll, maxHeight: 240 }}>\r\n <table style={s.table}>\r\n <thead>\r\n <tr>\r\n <th style={s.th}>表名</th>\r\n <th style={s.th}>行数</th>\r\n <th style={s.th}>列数</th>\r\n <th style={s.th}>主键</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {tables.length === 0 && !loading\r\n ? (\r\n <tr>\r\n <td style={{ ...s.td, color: c.fg2, textAlign: 'center', padding: '16px 8px' }} colSpan={4}>\r\n 没有匹配的表\r\n </td>\r\n </tr>\r\n )\r\n : tables.map(table => (\r\n <tr\r\n key={table.tableName}\r\n style={{ ...s.row, ...(table.tableName === selected ? s.selectedRow : null) }}\r\n onClick={() => setSelected(table.tableName === selected ? null : table.tableName)}\r\n >\r\n <td style={s.td} title={table.tableName}>{table.tableName}</td>\r\n <td style={s.td}>{formatRows(table.rowCount)}</td>\r\n <td style={s.td}>{table.columns.length}</td>\r\n <td style={s.td}>{table.primaryKey ?? '—'}</td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n {current !== null\r\n ? (\r\n <div style={{ marginTop: 10 }}>\r\n <div style={s.label}>{current.tableName} 的列结构({current.columns.length})</div>\r\n <div style={{ ...s.scroll, maxHeight: 180 }}>\r\n <table style={s.table}>\r\n <thead>\r\n <tr>\r\n <th style={s.th}>列名</th>\r\n <th style={s.th}>类型</th>\r\n <th style={s.th}>可空</th>\r\n <th style={s.th}>注释</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {current.columns.map(column => (\r\n <tr key={column.name}>\r\n <td style={s.td} title={column.name}>{column.name}</td>\r\n <td style={s.td}>{ADMIN_COLUMN_TYPE_LABELS[column.type] ?? column.type}</td>\r\n <td style={s.td}>{column.nullable ? '是' : '否'}</td>\r\n <td style={{ ...s.td, color: c.fg2 }} title={column.description ?? ''}>{column.description ?? '—'}</td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n </div>\r\n )\r\n : null}\r\n\r\n <div style={{ ...s.bar, marginTop: 12 }}>\r\n <select\r\n style={s.select}\r\n value={scopeKey}\r\n disabled={importing || selected === null}\r\n onChange={e => setScopeKey(e.target.value)}\r\n >\r\n {scopes.length === 0\r\n ? <option value=\"\">(暂无可用工作区)</option>\r\n : scopes.map(scope => <option key={scope.scopeKey} value={scope.scopeKey}>{scope.scopeKey}</option>)}\r\n </select>\r\n <input\r\n style={{ ...s.search, flex: 1, minWidth: 120 }}\r\n placeholder=\"数据集名称(默认取表名)\"\r\n value={name}\r\n disabled={importing}\r\n onChange={e => setName(e.target.value)}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{ ...s.primaryButton, ...(selected === null || scopeKey.length === 0 || importing ? s.disabledButton : null) }}\r\n disabled={selected === null || scopeKey.length === 0 || importing}\r\n onClick={() => void handleImport()}\r\n >{importing ? '导入中…' : '导入到工作区'}</button>\r\n </div>\r\n\r\n {result !== null\r\n ? (\r\n <div style={{ ...s.banner, marginTop: 8, marginBottom: 0, borderColor: c.ok, color: c.ok }}>\r\n <span>\r\n 已导入 {result.name}(datasetId: {result.datasetId}):{result.rowCount} 行 / {result.columnCount} 列\r\n {result.status === 'running' ? '(后台导入中,完成后会通知)' : ''}\r\n </span>\r\n </div>\r\n )\r\n : null}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 「数据源」页签:列表(名称 / 类型 / 地址 / 库名 / 连通状态 / 最近检测)+ 工具条\r\n * (搜索 / 新建 / 刷新)+ 选中项的详情面板。\r\n *\r\n * 详情面板上部位只读连接摘要(密码位只显示「已设置 · 不可查看」),下部位\r\n * `SourceTablesPanel`:浏览远端表并把选中的表导入到某个工作区。\r\n *\r\n * 写操作(新建 / 编辑 / 删除 / 导入)在只读模式或接口关闭时整体置灰 —— 判定由服务端\r\n * 返回的 `READ_ONLY` / `ADMIN_DISABLED` / `SOURCE_DISABLED` 错误码驱动,不靠前端猜。\r\n */\r\n\r\nimport { useCallback, useEffect, useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport {\r\n ADMIN_SOURCE_TYPE_LABELS,\r\n type DataSourceView,\r\n type ScopeView,\r\n} from '../../admin-contract'\r\nimport { AdminApiError, deleteDataSource, listDataSources, listScopes, testDataSource } from './api'\r\nimport { DataSourceForm } from './DataSourceForm'\r\nimport { SourceTablesPanel } from './SourceTablesPanel'\r\nimport { fmtAgo } from './DatasetTable'\r\nimport { c, s, SOURCE_STATUS_COLOR, SOURCE_STATUS_TEXT, TYPE_BADGE } from './styles'\r\n\r\ntype Mode = 'browse' | 'create' | 'edit'\r\n\r\nconst READ_ONLY_CODES = ['READ_ONLY', 'ADMIN_DISABLED', 'SOURCE_DISABLED']\r\n\r\nexport function DataSourcesPanel(): ReactElement {\r\n const [items, setItems] = useState<DataSourceView[]>([])\r\n const [scopes, setScopes] = useState<ScopeView[]>([])\r\n const [query, setQuery] = useState('')\r\n const [selectedId, setSelectedId] = useState<string | null>(null)\r\n const [mode, setMode] = useState<Mode>('browse')\r\n const [loading, setLoading] = useState(false)\r\n const [writable, setWritable] = useState(true)\r\n const [error, setError] = useState<string | null>(null)\r\n const [notice, setNotice] = useState<string | null>(null)\r\n const [testingId, setTestingId] = useState<string | null>(null)\r\n const [confirmId, setConfirmId] = useState<string | null>(null)\r\n\r\n const loadScopes = useCallback(async (): Promise<void> => {\r\n try {\r\n setScopes((await listScopes()).scopes)\r\n } catch {\r\n // 工作区列表拿不到不阻断数据源浏览;导入时再提示。\r\n }\r\n }, [])\r\n\r\n const loadList = useCallback(async (): Promise<void> => {\r\n setLoading(true)\r\n try {\r\n setItems((await listDataSources()).sources)\r\n setError(null)\r\n } catch (failure) {\r\n setError(failure instanceof Error ? failure.message : '加载数据源失败')\r\n if (failure instanceof AdminApiError && READ_ONLY_CODES.includes(failure.code)) setWritable(false)\r\n } finally {\r\n setLoading(false)\r\n }\r\n }, [])\r\n\r\n useEffect(() => {\r\n void loadScopes()\r\n void loadList()\r\n }, [loadScopes, loadList])\r\n\r\n const selected = items.find(item => item.id === selectedId) ?? null\r\n const needle = query.trim().toLowerCase()\r\n const visible = needle.length === 0\r\n ? items\r\n : items.filter(item => (\r\n item.name.toLowerCase().includes(needle)\r\n || item.type.toLowerCase().includes(needle)\r\n || item.host.toLowerCase().includes(needle)\r\n || item.database.toLowerCase().includes(needle)\r\n ))\r\n\r\n async function handleTest(id: string): Promise<void> {\r\n if (testingId !== null) return\r\n setTestingId(id)\r\n setNotice(null)\r\n try {\r\n const result = await testDataSource({ source: id })\r\n setNotice(result.success\r\n ? `连接成功(${result.latency}ms${result.version === null ? '' : ` · ${result.version}`})`\r\n : `连接失败:${result.error ?? '未知错误'}`)\r\n await loadList()\r\n } catch (failure) {\r\n setError(failure instanceof Error ? failure.message : '测试连接失败')\r\n } finally {\r\n setTestingId(null)\r\n }\r\n }\r\n\r\n async function handleDelete(id: string): Promise<void> {\r\n setConfirmId(null)\r\n try {\r\n await deleteDataSource(id)\r\n if (selectedId === id) {\r\n setSelectedId(null)\r\n setMode('browse')\r\n }\r\n await loadList()\r\n } catch (failure) {\r\n setError(failure instanceof Error ? failure.message : '删除失败')\r\n }\r\n }\r\n\r\n async function handleSaved(view: DataSourceView): Promise<void> {\r\n setMode('browse')\r\n setNotice(`已保存数据源「${view.name}」`)\r\n await loadList()\r\n setSelectedId(view.id)\r\n }\r\n\r\n function handleError(failure: Error): void {\r\n setError(failure.message)\r\n if (failure instanceof AdminApiError && READ_ONLY_CODES.includes(failure.code)) setWritable(false)\r\n }\r\n\r\n return (\r\n <div>\r\n <div style={s.toolbar}>\r\n <input\r\n style={s.search}\r\n placeholder=\"按名称 / 类型 / 主机 / 库名搜索…\"\r\n value={query}\r\n onChange={e => setQuery(e.target.value)}\r\n />\r\n <button\r\n type=\"button\"\r\n style={{ ...s.primaryButton, ...(!writable ? s.disabledButton : null) }}\r\n disabled={!writable}\r\n onClick={() => { setMode('create'); setSelectedId(null) }}\r\n title={writable ? '新建数据源' : '只读模式、接口已关闭或数据源功能未启用'}\r\n >+ 新建数据源</button>\r\n <button type=\"button\" style={s.button} disabled={loading} onClick={() => void loadList()}>刷新</button>\r\n </div>\r\n\r\n {error !== null\r\n ? (\r\n <div style={s.banner}>\r\n <span>{error}</span>\r\n <span style={{ ...s.link, marginLeft: 'auto' }} onClick={() => setError(null)}>忽略</span>\r\n </div>\r\n )\r\n : null}\r\n\r\n {notice !== null\r\n ? (\r\n <div style={{ ...s.banner, borderColor: c.ok, color: c.ok }}>\r\n <span>{notice}</span>\r\n <span style={{ ...s.link, marginLeft: 'auto' }} onClick={() => setNotice(null)}>忽略</span>\r\n </div>\r\n )\r\n : null}\r\n\r\n {confirmId !== null\r\n ? (\r\n <div style={s.banner}>\r\n <span>确认删除这个数据源?该操作不可恢复。</span>\r\n <button type=\"button\" style={{ ...s.dangerButton, marginLeft: 'auto' }} onClick={() => void handleDelete(confirmId)}>删除</button>\r\n <button type=\"button\" style={s.button} onClick={() => setConfirmId(null)}>取消</button>\r\n </div>\r\n )\r\n : null}\r\n\r\n <div style={s.scroll}>\r\n <table style={s.table}>\r\n <thead>\r\n <tr>\r\n <th style={s.th}>名称</th>\r\n <th style={s.th}>类型</th>\r\n <th style={s.th}>地址</th>\r\n <th style={s.th}>库名</th>\r\n <th style={s.th}>状态</th>\r\n <th style={s.th}>最近检测</th>\r\n <th style={s.th}>操作</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {visible.length === 0 && !loading\r\n ? (\r\n <tr>\r\n <td style={{ ...s.td, color: c.fg2, textAlign: 'center', padding: '18px 8px' }} colSpan={7}>\r\n 还没有数据源,点击「新建数据源」登记一个 MySQL / PostgreSQL 连接\r\n </td>\r\n </tr>\r\n )\r\n : visible.map(item => (\r\n <tr\r\n key={item.id}\r\n style={{ ...s.row, ...(item.id === selectedId ? s.selectedRow : null) }}\r\n onClick={() => { setSelectedId(item.id); setMode('browse') }}\r\n >\r\n <td style={s.td} title={item.description ?? ''}>{item.name}</td>\r\n <td style={s.td}>\r\n <span\r\n style={{\r\n display: 'inline-block', padding: '0 6px', borderRadius: 4, fontSize: 11,\r\n color: TYPE_BADGE[item.type] ?? c.fg2, border: `1px solid ${TYPE_BADGE[item.type] ?? c.border}`,\r\n }}\r\n >{ADMIN_SOURCE_TYPE_LABELS[item.type] ?? item.type}</span>\r\n </td>\r\n <td style={s.td} title={`${item.host}:${item.port}`}>{item.host}:{item.port}</td>\r\n <td style={s.td}>{item.database}</td>\r\n <td style={s.td}>\r\n <span style={{ ...s.dot, background: SOURCE_STATUS_COLOR[item.status] ?? c.fg2 }} />\r\n {SOURCE_STATUS_TEXT[item.status] ?? item.status}\r\n </td>\r\n <td style={{ ...s.td, color: c.fg2 }}>\r\n {item.lastCheckedAt === null ? '—' : fmtAgo(item.lastCheckedAt)}\r\n </td>\r\n <td style={{ ...s.td, whiteSpace: 'nowrap' }}>\r\n <span\r\n style={{ ...s.link, marginRight: 8, ...(testingId !== null ? s.disabledButton : null) }}\r\n onClick={event => {\r\n event.stopPropagation()\r\n if (testingId === null) void handleTest(item.id)\r\n }}\r\n >{testingId === item.id ? '测试中…' : '测试'}</span>\r\n <span\r\n style={{ ...s.link, marginRight: 8, ...(!writable ? s.disabledButton : null) }}\r\n onClick={event => {\r\n event.stopPropagation()\r\n if (!writable) return\r\n setSelectedId(item.id)\r\n setMode('edit')\r\n }}\r\n >编辑</span>\r\n <span\r\n style={{ ...s.link, color: c.danger, ...(!writable ? s.disabledButton : null) }}\r\n onClick={event => {\r\n event.stopPropagation()\r\n if (writable) setConfirmId(item.id)\r\n }}\r\n >删除</span>\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n {mode === 'create'\r\n ? (\r\n <DataSourceForm\r\n onSaved={view => void handleSaved(view)}\r\n onCancel={() => setMode('browse')}\r\n onError={handleError}\r\n />\r\n )\r\n : mode === 'edit' && selected !== null\r\n ? (\r\n <DataSourceForm\r\n initial={selected}\r\n onSaved={view => void handleSaved(view)}\r\n onCancel={() => setMode('browse')}\r\n onError={handleError}\r\n />\r\n )\r\n : selected !== null\r\n ? (\r\n <>\r\n <div style={s.panel}>\r\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>\r\n <strong style={{ fontSize: 13 }}>{selected.name}</strong>\r\n <span style={s.muted}>\r\n {ADMIN_SOURCE_TYPE_LABELS[selected.type] ?? selected.type} · 最近更新 {fmtAgo(selected.updatedAt)}\r\n </span>\r\n </div>\r\n <div style={s.grid2}>\r\n <div><span style={s.muted}>地址:</span>{selected.host}:{selected.port}</div>\r\n <div><span style={s.muted}>库名:</span>{selected.database}</div>\r\n <div><span style={s.muted}>用户名:</span>{selected.username}</div>\r\n <div>\r\n <span style={s.muted}>密码:</span>\r\n {selected.hasPassword ? '已设置 · 不可查看' : '未设置'}\r\n </div>\r\n <div><span style={s.muted}>SSL:</span>{selected.sslMode ?? '—'}</div>\r\n <div><span style={s.muted}>连接池上限:</span>{selected.poolMax ?? '—'}</div>\r\n </div>\r\n {selected.description !== null\r\n ? <div style={{ ...s.muted, marginTop: 6 }}>{selected.description}</div>\r\n : null}\r\n {selected.lastError !== null\r\n ? <div style={{ ...s.muted, marginTop: 6, color: c.danger }}>最近错误:{selected.lastError}</div>\r\n : null}\r\n </div>\r\n <SourceTablesPanel source={selected} scopes={scopes} onError={handleError} />\r\n </>\r\n )\r\n : (\r\n <div style={{ ...s.muted, marginTop: 12 }}>\r\n {loading ? '加载中…' : '选择一个数据源查看详情与远端表,或点击「新建数据源」。'}\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 设置页「数据集」根组件:顶部页签条 + 两个同级面板。\r\n *\r\n * - 「数据集」:跨工作区聚合浏览 / 新建 / 编辑 / 查看表数据(既有能力,见 `DatasetsPanel`);\r\n * - 「数据源」:登记与管理远程数据库连接,浏览远端表并导入成数据集(见 `DataSourcesPanel`)。\r\n *\r\n * 两个面板各自持有状态(搜索、分页、选中项),切换页签不互相重置。\r\n */\r\n\r\nimport { useState } from 'react'\r\nimport type { ReactElement } from 'react'\r\nimport { DatasetsPanel } from './DatasetsPanel'\r\nimport { DataSourcesPanel } from './DataSourcesPanel'\r\nimport { s } from './styles'\r\n\r\ntype Tab = 'datasets' | 'sources'\r\n\r\nconst TABS: { key: Tab; label: string }[] = [\r\n { key: 'datasets', label: '数据集' },\r\n { key: 'sources', label: '数据源' },\r\n]\r\n\r\nexport function DatasetSettingsSection(): ReactElement {\r\n const [tab, setTab] = useState<Tab>('datasets')\r\n\r\n return (\r\n <div style={s.page}>\r\n <div style={s.tabs}>\r\n {TABS.map(entry => (\r\n <button\r\n key={entry.key}\r\n type=\"button\"\r\n style={{ ...s.tab, ...(tab === entry.key ? s.activeTab : null) }}\r\n onClick={() => setTab(entry.key)}\r\n >{entry.label}</button>\r\n ))}\r\n </div>\r\n {tab === 'datasets' ? <DatasetsPanel /> : <DataSourcesPanel />}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * 浏览器半身:`dataset_query` 的结果表格卡片 —— 设计文档 §7。\r\n *\r\n * 它接管 `tool.call.toolview` 上 key = `dataset_query` 的渲染位,按工具结果里的\r\n * `meta`(`output.presentationMeta` 产出,模型不可见)自动拉取并展示完整结果:\r\n * 翻页、排序、过期提示,全部只带 `viewId`,不接触任何 SQL 或物理表名。\r\n *\r\n * 构建契约(与 dsh 的 client bundle preset 一致):\r\n * - 产物是 CJS 工厂,由 `window.__ModuleLoader__.load({ id, factory })` 注册;\r\n * - `react` 是平台 seed 词,运行时由模块表提供,打包时保持 external;\r\n * - 只能用基线能力(React + cordis slots),不请求任何额外工作区包。\r\n */\r\n\r\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\r\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\r\nimport { ADMIN_SECTION_ID, ADMIN_SECTION_LABEL, ADMIN_SECTION_ORDER } from '../admin-contract'\r\nimport { DatasetSettingsSection } from './settings/Section'\r\n\r\n/** 本插件注册的工具名,也是 `tool.call.toolview` 的 keyed 派发键。 */\r\nconst TOOL_KEY = 'dataset_query'\r\nconst TOOLVIEW_SLOT = 'tool.call.toolview'\r\n\r\n// ── 平台面(只声明本组件实际用到的部分) ──────────────────────────────────\r\n\r\ninterface SlotRegistry {\r\n inject(slot: string, setup: () => () => void): void\r\n // spec 是开放对象:toolview 用 key,settings.section 用 id/order/label 等。\r\n register(spec: { name: string; [key: string]: unknown }, component: unknown): () => void\r\n}\r\n\r\ninterface ToolCallViewProps {\r\n toolName: string\r\n /** 冻结中的调用或已落地的结果节点。 */\r\n block: {\r\n meta?: unknown\r\n content?: { type: string; text?: string }[]\r\n }\r\n}\r\n\r\n// ── 视图元数据契约(与 view.ts 的 ViewDescriptor 对齐) ───────────────────\r\n\r\ninterface ViewColumn { name: string; type: string }\r\n\r\ninterface DatasetViewMeta {\r\n kind: string\r\n viewId: string\r\n endpoint: string\r\n datasetId: string\r\n name: string\r\n columns: ViewColumn[]\r\n totalRows: number\r\n pageSize: number\r\n maxPageSize: number\r\n stable: boolean\r\n sortable: string[]\r\n}\r\n\r\ninterface PagePayload {\r\n columns: string[]\r\n rows: Record<string, unknown>[]\r\n page: number\r\n pageSize: number\r\n totalRows: number\r\n totalPages: number\r\n}\r\n\r\nfunction asViewMeta(value: unknown): DatasetViewMeta | undefined {\r\n if (typeof value !== 'object' || value === null) return undefined\r\n const meta = value as Partial<DatasetViewMeta>\r\n if (meta.kind !== 'dataset-view') return undefined\r\n if (typeof meta.viewId !== 'string' || typeof meta.endpoint !== 'string') return undefined\r\n return meta as DatasetViewMeta\r\n}\r\n\r\n// ── 样式(内联,避免引入 CSS 管线) ────────────────────────────────────────\r\n\r\nconst styles = {\r\n wrap: { margin: '8px 0', fontSize: 12, color: 'var(--dsw-fg-secondary, #666)' },\r\n head: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 },\r\n title: { fontWeight: 600, color: 'var(--dsw-fg, inherit)' },\r\n scroll: { maxHeight: 420, overflow: 'auto', border: '1px solid var(--dsw-border, #e5e5e5)', borderRadius: 6 },\r\n table: { borderCollapse: 'collapse', width: '100%', fontSize: 12 },\r\n th: {\r\n position: 'sticky', top: 0, background: 'var(--dsw-bg-subtle, #fafafa)',\r\n textAlign: 'left', padding: '6px 8px', borderBottom: '1px solid var(--dsw-border, #e5e5e5)',\r\n whiteSpace: 'nowrap',\r\n } as const,\r\n thButton: { background: 'none', border: 'none', cursor: 'pointer', font: 'inherit', padding: 0, color: 'inherit' },\r\n td: { padding: '4px 8px', borderBottom: '1px solid var(--dsw-border, #eee)', maxWidth: 320, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } as const,\r\n bar: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 6, flexWrap: 'wrap' },\r\n button: { border: '1px solid var(--dsw-border, #ddd)', background: 'transparent', borderRadius: 4, padding: '2px 8px', cursor: 'pointer', fontSize: 12 },\r\n muted: { opacity: 0.7 },\r\n error: { color: 'var(--dsw-danger, #c0392b)' },\r\n warn: { background: '#FEF3C7', color: '#92400E', padding: '6px 10px', borderRadius: 6, fontSize: 12, marginBottom: 6 },\r\n} as const\r\n\r\nfunction formatCell(value: unknown): string {\r\n if (value === null || value === undefined) return ''\r\n if (typeof value === 'object') {\r\n try {\r\n return JSON.stringify(value)\r\n } catch {\r\n return '[object]'\r\n }\r\n }\r\n return String(value)\r\n}\r\n\r\n// ── CSV 导出(纯字符串构造,不引入任何依赖) ─────────────────────────────\r\n\r\n/** 命中即需要加引号转义的字符:逗号、双引号、回车、换行。 */\r\nconst CSV_NEEDS_QUOTE = /[\",\\r\\n]/\r\n/** Excel 公式注入的高危前缀;只对文本值生效,负数等数值原样导出。 */\r\nconst CSV_RISKY_PREFIX = /^[=+\\-@\\t\\r]/\r\n\r\nfunction toCsvCell(value: unknown): string {\r\n let text = formatCell(value)\r\n if (text.length === 0) return ''\r\n // 前置单引号:Excel 会把它当公式执行,内容本身不变。\r\n if (typeof value === 'string' && CSV_RISKY_PREFIX.test(text)) text = `'${text}`\r\n if (CSV_NEEDS_QUOTE.test(text)) text = `\"${text.replace(/\"/g, '\"\"')}\"`\r\n return text\r\n}\r\n\r\nfunction toCsvRow(cells: unknown[]): string {\r\n return cells.map(cell => toCsvCell(cell)).join(',')\r\n}\r\n\r\n/** 表头 + 数据行;行分隔用 `\\r\\n` 且末行收尾,Excel / Numbers 都不会串列。 */\r\nfunction toCsv(columns: string[], rows: Record<string, unknown>[]): string {\r\n const lines = [toCsvRow(columns)]\r\n for (const row of rows) lines.push(toCsvRow(columns.map(column => row[column])))\r\n return `${lines.join('\\r\\n')}\\r\\n`\r\n}\r\n\r\n/** 只拼数据行:导出逐页追加时用(表头由 `toCsv` 的首页分片带出)。 */\r\nfunction toCsvRows(columns: string[], rows: Record<string, unknown>[]): string {\r\n if (rows.length === 0) return ''\r\n return `${rows.map(row => toCsvRow(columns.map(column => row[column]))).join('\\r\\n')}\\r\\n`\r\n}\r\n\r\n/** 文件名 `名称-YYYYMMDD-HHmmss.csv`,剔除文件系统不接受与控制字符。 */\r\nfunction buildCsvFileName(name: string, now: Date = new Date()): string {\r\n const pad = (value: number): string => String(value).padStart(2, '0')\r\n const stamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`\r\n + `-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`\r\n const safe = name.replace(/[\\\\/:*?\"<>|\\r\\n\\t]/g, '_').trim()\r\n return `${safe.length > 0 ? safe : 'dataset'}-${stamp}.csv`\r\n}\r\n\r\n/** Blob + 临时 `<a download>` 触发下载;前置 BOM 让 Excel 认成 UTF-8。 */\r\nfunction downloadCsv(filename: string, text: string): void {\r\n const url = URL.createObjectURL(new Blob([`\\uFEFF${text}`], { type: 'text/csv;charset=utf-8' }))\r\n const link = document.createElement('a')\r\n link.href = url\r\n link.download = filename\r\n link.style.display = 'none'\r\n document.body.appendChild(link)\r\n link.click()\r\n link.remove()\r\n // 立刻回收会让部分浏览器来不及取流,延后一拍释放。\r\n setTimeout(() => URL.revokeObjectURL(url), 0)\r\n}\r\n\r\n// ── 卡片 ──────────────────────────────────────────────────────────────────\r\n\r\ninterface CardState {\r\n status: 'loading' | 'ready' | 'error'\r\n payload?: PagePayload\r\n message?: string\r\n}\r\n\r\n/** 导出进度与结果(与分页取数状态分开,互不覆盖)。 */\r\ninterface ExportState {\r\n running: boolean\r\n progress?: { done: number; total: number }\r\n error?: string\r\n}\r\n\r\nfunction DatasetViewCard(props: ToolCallViewProps): React.ReactElement | null {\r\n const meta = useMemo(() => asViewMeta(props.block?.meta), [props.block])\r\n const [page, setPage] = useState(1)\r\n const [sort, setSort] = useState<{ column: string; order: 'asc' | 'desc' } | undefined>(undefined)\r\n const [state, setState] = useState<CardState>({ status: 'loading' })\r\n const requestId = useRef(0)\r\n // 导出走独立通道:翻页不打断它,只有卸载与排序变更才中止。\r\n const exportAbort = useRef<AbortController | undefined>(undefined)\r\n const [exportState, setExportState] = useState<ExportState>({ running: false })\r\n\r\n useEffect(() => () => exportAbort.current?.abort(), [])\r\n\r\n useEffect(() => {\r\n if (meta === undefined) return\r\n const controller = new AbortController()\r\n const id = requestId.current + 1\r\n requestId.current = id\r\n setState(previous => previous.status === 'ready' ? { ...previous, status: 'loading' } : previous)\r\n\r\n const url = new URL(meta.endpoint, window.location.origin)\r\n url.searchParams.set('page', String(page))\r\n url.searchParams.set('pageSize', String(meta.pageSize))\r\n if (sort !== undefined) {\r\n url.searchParams.set('sort', sort.column)\r\n url.searchParams.set('order', sort.order)\r\n }\r\n\r\n void (async () => {\r\n try {\r\n const response = await fetch(url.toString(), { signal: controller.signal, credentials: 'same-origin' })\r\n if (!response.ok) {\r\n const body = await response.json().catch(() => null) as { error?: { code?: string } } | null\r\n if (id === requestId.current) {\r\n // 出错时保留上一页 payload,表格不清空,仅顶部显示软提示。\r\n setState(previous => ({\r\n status: 'error',\r\n payload: previous.payload,\r\n message: body?.error?.code === 'VIEW_NOT_FOUND' || response.status === 404\r\n ? '数据接口不可用(请检查视图路由前缀配置或插件是否已启用视图)'\r\n : `取数失败(HTTP ${response.status})`,\r\n }))\r\n }\r\n return\r\n }\r\n const payload = await response.json() as PagePayload\r\n if (id === requestId.current) setState({ status: 'ready', payload })\r\n } catch (error) {\r\n if (controller.signal.aborted) return\r\n if (id === requestId.current) setState({ status: 'error', message: `取数失败:${String(error)}` })\r\n }\r\n })()\r\n\r\n return () => controller.abort()\r\n }, [meta, page, sort])\r\n\r\n const toggleSort = useCallback((column: string) => {\r\n if (meta === undefined || !meta.sortable.includes(column)) return\r\n // 顺序变了,正在导出的那份行序就作废。\r\n if (exportAbort.current !== undefined) {\r\n exportAbort.current.abort()\r\n exportAbort.current = undefined\r\n setExportState({ running: false })\r\n }\r\n setPage(1)\r\n setSort(previous => previous?.column === column\r\n ? { column, order: previous.order === 'asc' ? 'desc' : 'asc' }\r\n : { column, order: 'asc' })\r\n }, [meta])\r\n\r\n /**\r\n * 导出全部可浏览行为 CSV:把 `pageSize` 顶到 `maxPageSize`,从第 1 页顺序翻到\r\n * 末页,不受当前页码/每页行数限制。每页转成字符串分片后即丢掉行对象,\r\n * 避免几万行对象同时常驻;最后拼一次 Blob 触发下载。\r\n */\r\n const exportCsv = useCallback(() => {\r\n if (meta === undefined) return\r\n exportAbort.current?.abort()\r\n const controller = new AbortController()\r\n exportAbort.current = controller\r\n setExportState({ running: true, progress: { done: 0, total: meta.totalRows } })\r\n\r\n void (async () => {\r\n try {\r\n const base = new URL(meta.endpoint, window.location.origin)\r\n base.searchParams.set('pageSize', String(Math.max(1, Math.floor(meta.maxPageSize > 0 ? meta.maxPageSize : meta.pageSize))))\r\n if (sort !== undefined) {\r\n base.searchParams.set('sort', sort.column)\r\n base.searchParams.set('order', sort.order)\r\n }\r\n\r\n const chunks: string[] = []\r\n let exportColumns = meta.columns.map(column => column.name)\r\n let totalRows = meta.totalRows\r\n let totalPages = 1\r\n let done = 0\r\n\r\n for (let index = 1; index <= totalPages; index += 1) {\r\n if (controller.signal.aborted) return\r\n const url = new URL(base.toString())\r\n url.searchParams.set('page', String(index))\r\n const response = await fetch(url.toString(), { signal: controller.signal, credentials: 'same-origin' })\r\n if (!response.ok) {\r\n const body = await response.json().catch(() => null) as { error?: { code?: string } } | null\r\n if (controller.signal.aborted) return\r\n throw new Error(body?.error?.code === 'VIEW_NOT_FOUND' || response.status === 404\r\n ? '数据接口不可用(请检查视图路由前缀配置或插件是否已启用视图)'\r\n : `导出失败(HTTP ${response.status})`)\r\n }\r\n const current = await response.json() as PagePayload\r\n if (controller.signal.aborted) return\r\n if (index === 1) {\r\n // 列集合与总页数只认首屏,避免中途数据变化导致串列。\r\n exportColumns = current.columns.length > 0 ? current.columns : exportColumns\r\n totalRows = current.totalRows\r\n totalPages = Math.max(1, current.totalPages)\r\n chunks.push(toCsv(exportColumns, current.rows))\r\n } else {\r\n chunks.push(toCsvRows(exportColumns, current.rows))\r\n }\r\n done += current.rows.length\r\n setExportState({ running: true, progress: { done, total: totalRows } })\r\n }\r\n\r\n downloadCsv(buildCsvFileName(meta.name), chunks.join(''))\r\n if (exportAbort.current === controller) exportAbort.current = undefined\r\n setExportState({ running: false })\r\n } catch (error) {\r\n if (controller.signal.aborted) return\r\n if (exportAbort.current === controller) exportAbort.current = undefined\r\n setExportState({ running: false, error: error instanceof Error ? error.message : `导出失败:${String(error)}` })\r\n }\r\n })()\r\n }, [meta, sort])\r\n\r\n // 无视图(小结果集 / 主机未启用视图):退化为纯文本,不发起任何请求。\r\n if (meta === undefined) {\r\n const text = (props.block?.content ?? [])\r\n .filter(block => block.type === 'text')\r\n .map(block => block.text ?? '')\r\n .join('\\n')\r\n return React.createElement('div', { style: styles.wrap }, React.createElement('pre', { style: { margin: 0, whiteSpace: 'pre-wrap' } }, text))\r\n }\r\n\r\n const payload = state.payload\r\n const columns = payload?.columns ?? meta.columns.map(column => column.name)\r\n const rows = payload?.rows ?? []\r\n const totalPages = payload?.totalPages ?? Math.max(1, Math.ceil(meta.totalRows / meta.pageSize))\r\n const busy = state.status === 'loading' && payload === undefined\r\n // 服务端把 page 夹到分页边界内,首屏拿到 totalPages 后即可反推可浏览的行数上限。\r\n const servableRows = payload === undefined ? meta.totalRows : payload.totalPages * payload.pageSize\r\n\r\n const head = React.createElement('tr', null, ...columns.map(column => React.createElement(\r\n 'th',\r\n { key: column, style: styles.th },\r\n meta.sortable.includes(column)\r\n ? React.createElement('button', {\r\n type: 'button',\r\n style: styles.thButton,\r\n onClick: () => toggleSort(column),\r\n title: '按该列排序',\r\n }, `${column}${sort?.column === column ? (sort.order === 'asc' ? ' ↑' : ' ↓') : ''}`)\r\n : column,\r\n )))\r\n\r\n const body = rows.map((row, index) => React.createElement('tr', { key: String(row._row_id ?? index) },\r\n ...columns.map(column => React.createElement('td', { key: column, style: styles.td, title: formatCell(row[column]) }, formatCell(row[column])))))\r\n\r\n return React.createElement('div', { style: styles.wrap },\r\n React.createElement('div', { style: styles.warn }, '数据可能已发生变化,以最新查询结果为准'),\r\n React.createElement('div', { style: styles.head },\r\n React.createElement('span', { style: styles.title }, `数据集 ${meta.name}`),\r\n React.createElement('span', null, `共 ${meta.totalRows} 行 · ${meta.columns.length} 列`),\r\n servableRows < meta.totalRows\r\n ? React.createElement('span', { style: styles.muted }, `(仅可浏览前 ${servableRows} 行)`)\r\n : null,\r\n state.status === 'error' ? React.createElement('span', { style: styles.error }, state.message ?? '') : null,\r\n busy ? React.createElement('span', { style: styles.muted }, '加载中…') : null,\r\n ),\r\n React.createElement('div', { style: styles.scroll },\r\n React.createElement('table', { style: styles.table },\r\n React.createElement('thead', null, head),\r\n React.createElement('tbody', null, ...body))),\r\n React.createElement('div', { style: styles.bar },\r\n React.createElement('button', { type: 'button', style: styles.button, disabled: page <= 1, onClick: () => setPage(1) }, '« 首页'),\r\n React.createElement('button', { type: 'button', style: styles.button, disabled: page <= 1, onClick: () => setPage(current => Math.max(1, current - 1)) }, '上一页'),\r\n React.createElement('span', null, `第 ${payload?.page ?? page} / ${totalPages} 页`),\r\n React.createElement('button', {\r\n type: 'button',\r\n style: styles.button,\r\n disabled: !meta.stable || page >= totalPages,\r\n onClick: () => setPage(current => Math.min(totalPages, current + 1)),\r\n }, '下一页'),\r\n !meta.stable ? React.createElement('span', { style: styles.muted }, '(该查询未声明稳定排序,仅展示首页)') : null,\r\n React.createElement('button', {\r\n type: 'button',\r\n style: styles.button,\r\n disabled: exportState.running || state.status === 'error',\r\n title: `按当前排序导出全部可浏览行(${servableRows} 行)为 CSV`,\r\n onClick: exportCsv,\r\n }, exportState.running\r\n ? `导出中…(${exportState.progress?.done ?? 0} / ${exportState.progress?.total ?? servableRows} 行)`\r\n : '导出 CSV'),\r\n exportState.error === undefined\r\n ? null\r\n : React.createElement('span', { style: styles.error }, exportState.error),\r\n ))\r\n}\r\n\r\n// ── cordis 插件(浏览器半身) ─────────────────────────────────────────────\r\n\r\nexport const name = 'dsh-lh-data'\r\nexport const inject = ['slots']\r\n\r\nexport function apply(ctx: ClientContext): void {\r\n const slots = (ctx as unknown as { slots?: SlotRegistry }).slots\r\n if (slots === undefined) return\r\n slots.inject(TOOLVIEW_SLOT, () => slots.register({ name: TOOLVIEW_SLOT, key: TOOL_KEY }, DatasetViewCard))\r\n // 设置面板新增「数据集」菜单(外部插件可自由注册该 list slot)。\r\n slots.inject('settings.section', () =>\r\n slots.register(\r\n { name: 'settings.section', id: ADMIN_SECTION_ID, order: ADMIN_SECTION_ORDER, label: ADMIN_SECTION_LABEL },\r\n DatasetSettingsSection,\r\n ))\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAmBa,qBAAqB;MAGrBA,kBAA0B,EAAE,mBAAmB;MAQ/C,mBAAmB;MACnB,sBAAsB;MACtB,sBAAsB;MAItB,kBAAkB;MAMlB,sBAAsB;MAEtB,0BAA0B;MAE1B,sBAAsB;MAOtB,oBAAoB;MAGpB,0BAA0B;MAgB1BC,qBAAwC,CAAC,SAAS,YAAa;MAE/DC,2BAA6D;CACxE,OAAO;CACP,YAAY;AACb;MAGYC,6BAA+D;CAC1E,OAAO;CACP,YAAY;AACb;MAQYC,2BAAsE;CACjF,MAAM;CACN,SAAS;CACT,SAAS;CACT,MAAM;AACP;MAGYC,qBAAiD;CAAC;CAAQ;CAAW;CAAW;AAAO;;;;ICvEvF,gBAAN,cAA4B,MAAM;CACvC,YACkBC,QACAC,MAChBC,SACA;AACA,QAAM,QAAQ;AAAA,OAJE,SAAA;AAAA,OACA,OAAA;AAIhB,OAAK,OAAO;CACb;AACF;AAED,eAAe,QAAWC,MAAcC,MAAgC;CACtE,MAAM,WAAW,MAAM,OAAO,EAAE,eAAe,EAAE,KAAK,GAAG;EACvD,GAAG;EACH,aAAa;EACb,SAAS;GAAE,gBAAgB;GAAoB,GAAI,MAAM,WAAW,CAAE;EAAG;CAC1E,EAAC;AACF,MAAK,SAAS,IAAI;EAChB,IAAI,OAAO;EACX,IAAI,WAAW,YAAY,SAAS,OAAO;AAC3C,MAAI;GACF,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,OAAI,KAAK,OAAO;AACd,WAAO,KAAK,MAAM,QAAQ;AAC1B,cAAU,KAAK,MAAM,WAAW;GACjC;EACF,QAAO,CAEP;AACD,QAAM,IAAI,cAAc,SAAS,QAAQ,MAAM;CAChD;AACD,QAAQ,MAAM,SAAS,MAAM;AAC9B;AAEM,SAAS,aAA+C;AAC7D,QAAO,QAAQ,UAAU;AAC1B;AAEM,SAAS,aAAaC,OAAeC,MAAcC,UAA+C;CACvG,MAAM,SAAS,IAAI;AACnB,KAAI,MAAM,MAAM,CAAC,SAAS,EAAG,QAAO,IAAI,KAAK,MAAM;AACnD,QAAO,IAAI,QAAQ,OAAO,KAAK,CAAC;AAChC,QAAO,IAAI,YAAY,OAAO,SAAS,CAAC;AACxC,QAAO,SAAS,YAAY,OAAO,UAAU,CAAC,EAAE;AACjD;AAEM,SAAS,WAAWC,IAAYC,OAA2C;AAChF,QAAO,SAAS,YAAY,mBAAmB,GAAG,CAAC,SAAS,mBAAmB,MAAM,CAAC,EAAE;AACzF;AAGM,SAAS,gBACdD,IACAC,OACAH,MACAC,UAC4B;CAC5B,MAAM,SAAS,IAAI;AACnB,QAAO,IAAI,SAAS,MAAM;AAC1B,QAAO,IAAI,QAAQ,OAAO,KAAK,CAAC;AAChC,QAAO,IAAI,YAAY,OAAO,SAAS,CAAC;AACxC,QAAO,SAAS,YAAY,mBAAmB,GAAG,CAAC,QAAQ,OAAO,UAAU,CAAC,EAAE;AAChF;AAEM,SAAS,cAAcG,MAAwD;AACpF,QAAO,QAAQ,aAAa;EAAE,QAAQ;EAAQ,MAAM,KAAK,UAAU,KAAK;CAAE,EAAC;AAC5E;AAEM,SAAS,aACdF,IACAC,OACAE,MAC4B;AAC5B,QAAO,SAAS,YAAY,mBAAmB,GAAG,CAAC,SAAS,mBAAmB,MAAM,CAAC,GAAG;EACvF,QAAQ;EACR,MAAM,KAAK,UAAU,KAAK;CAC3B,EAAC;AACH;AAEM,SAAS,cACdH,IACAC,OACyD;AACzD,QAAO,SAAS,YAAY,mBAAmB,GAAG,CAAC,SAAS,mBAAmB,MAAM,CAAC,GAAG,EACvF,QAAQ,SACT,EAAC;AACH;AAIM,SAAS,kBAA0D;AACxE,QAAO,QAAQ,WAAW;AAC3B;AAMM,SAAS,iBAAiBG,MAAwD;AACvF,QAAO,QAAQ,YAAY;EAAE,QAAQ;EAAQ,MAAM,KAAK,UAAU,KAAK;CAAE,EAAC;AAC3E;AAEM,SAAS,gBAAgBJ,IAAYK,MAAuD;AACjG,QAAO,SAAS,WAAW,mBAAmB,GAAG,CAAC,GAAG;EAAE,QAAQ;EAAS,MAAM,KAAK,UAAU,KAAK;CAAE,EAAC;AACtG;AAEM,SAAS,iBAAiBL,IAA2C;AAC1E,QAAO,SAAS,WAAW,mBAAmB,GAAG,CAAC,GAAG,EAAE,QAAQ,SAAU,EAAC;AAC3E;AAGM,SAAS,eAAeM,MAA0D;AACvF,QAAO,QAAQ,iBAAiB;EAAE,QAAQ;EAAQ,MAAM,KAAK,UAAU,KAAK;CAAE,EAAC;AAChF;AAEM,SAAS,iBAAiBN,IAAYO,QAAiBC,OAAiD;CAC7G,MAAM,SAAS,IAAI;AACnB,KAAI,WAAW,aAAa,OAAO,MAAM,CAAC,SAAS,EAAG,QAAO,IAAI,UAAU,OAAO;AAClF,KAAI,UAAU,aAAa,MAAM,MAAM,CAAC,SAAS,EAAG,QAAO,IAAI,KAAK,MAAM;CAC1E,MAAM,SAAS,OAAO,UAAU,CAAC,SAAS,KAAK,GAAG,OAAO,UAAU,CAAC,IAAI;AACxE,QAAO,SAAS,WAAW,mBAAmB,GAAG,CAAC,SAAS,OAAO,EAAE;AACrE;AAEM,SAAS,kBAAkBR,IAAYS,MAAkE;AAC9G,QAAO,SAAS,WAAW,mBAAmB,GAAG,CAAC,UAAU;EAAE,QAAQ;EAAQ,MAAM,KAAK,UAAU,KAAK;CAAE,EAAC;AAC5G;;;;MCnJY,IAAI;CACf,IAAI;CACJ,QAAQ;CACR,IAAI;CACJ,KAAK;CACL,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,IAAI;CACJ,MAAM;AACP;MAEY,IAAI;CACf,MAAM;EAAE,UAAU;EAAI,OAAO,EAAE;EAAI,YAAY;CAAK;CACpD,SAAS;EACP,SAAS;EAAQ,YAAY;EAAU,KAAK;EAAG,cAAc;EAC7D,eAAe;EAAI,eAAe,YAAY,EAAE,OAAO;CACxD;CACD,QAAQ;EACN,MAAM;EAAG,UAAU;EAAG,SAAS;EAAW,UAAU;EACpD,SAAS,YAAY,EAAE,OAAO;EAAG,cAAc;EAAG,YAAY,EAAE;EAAI,OAAO,EAAE;EAC7E,SAAS;CACV;CACD,QAAQ;EACN,SAAS,YAAY,EAAE,OAAO;EAAG,YAAY,EAAE;EAAI,OAAO,EAAE;EAC5D,cAAc;EAAG,SAAS;EAAY,QAAQ;EAAW,UAAU;EAAI,YAAY;CACpF;CACD,eAAe;EACb,SAAS,YAAY,EAAE,QAAQ;EAAG,YAAY,EAAE;EAAS,OAAO;EAChE,cAAc;EAAG,SAAS;EAAY,QAAQ;EAAW,UAAU;EAAI,YAAY;CACpF;CACD,cAAc;EACZ,SAAS,YAAY,EAAE,OAAO;EAAG,YAAY;EAAe,OAAO,EAAE;EACrE,cAAc;EAAG,SAAS;EAAY,QAAQ;EAAW,UAAU;CACpE;CACD,gBAAgB;EAAE,SAAS;EAAK,QAAQ;CAAe;CACvD,QAAQ;EACN,cAAc;EAAI,SAAS;EAAY,cAAc;EACrD,SAAS,YAAY,EAAE,OAAO;EAAG,OAAO,EAAE;EAAQ,UAAU;EAC5D,SAAS;EAAQ,YAAY;EAAU,KAAK;CAC7C;CACD,QAAQ;EACN,WAAW;EAAK,UAAU;EAAQ,SAAS,YAAY,EAAE,OAAO;EAAG,cAAc;CAClF;CACD,OAAO;EAAE,gBAAgB;EAAY,OAAO;EAAQ,UAAU;CAAI;CAClE,IAAI;EACF,UAAU;EAAU,KAAK;EAAG,YAAY,EAAE;EAAQ,WAAW;EAC7D,SAAS;EAAW,eAAe,YAAY,EAAE,OAAO;EAAG,OAAO,EAAE;EAAK,YAAY;EACrF,YAAY;CACb;CACD,IAAI;EACF,SAAS;EAAW,eAAe,YAAY,EAAE,OAAO;EAAG,UAAU;EACrE,UAAU;EAAU,cAAc;EAAY,YAAY;CAC3D;CACD,KAAK,EAAE,QAAQ,UAAW;CAC1B,aAAa,EAAE,YAAY,gBAAgB,EAAE,QAAQ,EAAG;CACxD,UAAU,EAAE,YAAY,EAAE,OAAQ;CAClC,KAAK;EAAE,SAAS;EAAgB,OAAO;EAAG,QAAQ;EAAG,cAAc;EAAO,aAAa;CAAG;CAC1F,OAAO;EACL,WAAW;EAAI,SAAS;EAAI,SAAS,YAAY,EAAE,OAAO;EAAG,cAAc;EAAG,YAAY,EAAE;CAC7F;CACD,OAAO,EAAE,cAAc,GAAI;CAC3B,OAAO;EAAE,SAAS;EAAS,UAAU;EAAI,OAAO,EAAE;EAAK,cAAc;CAAG;CACxE,OAAO;EACL,OAAO;EAAQ,WAAW;EAAc,SAAS;EAAW,UAAU;EACtE,SAAS,YAAY,EAAE,OAAO;EAAG,cAAc;EAAG,YAAY,EAAE;EAAI,OAAO,EAAE;EAAI,SAAS;CAC3F;CACD,QAAQ;EACN,SAAS;EAAW,UAAU;EAAI,SAAS,YAAY,EAAE,OAAO;EAAG,cAAc;EACjF,YAAY,EAAE;EAAI,OAAO,EAAE;EAAI,SAAS;CACzC;CACD,UAAU;EACR,OAAO;EAAQ,WAAW;EAAc,SAAS;EAAW,UAAU;EACtE,SAAS,YAAY,EAAE,OAAO;EAAG,cAAc;EAAG,YAAY,EAAE;EAAI,OAAO,EAAE;EAC7E,SAAS;EAAQ,QAAQ;EAAY,WAAW;EAAI,YAAY;CACjE;CACD,OAAO;EAAE,SAAS;EAAQ,qBAAqB;EAAW,KAAK;CAAI;CACnE,OAAO;EAAE,SAAS;EAAQ,qBAAqB;EAAe,KAAK;EAAG,YAAY;CAAO;CACzF,MAAM;EAAE,SAAS;EAAQ,gBAAgB;EAAiB,YAAY;EAAU,cAAc;CAAI;CAClG,KAAK;EAAE,SAAS;EAAQ,YAAY;EAAU,KAAK;EAAG,WAAW;EAAG,UAAU;CAAQ;CACtF,OAAO,EAAE,OAAO,EAAE,IAAK;CACvB,MAAM;EAAE,OAAO,EAAE;EAAS,QAAQ;CAAW;CAC7C,QAAQ,EAAE,YAAY,IAAK;CAC3B,MAAM;EACJ,SAAS;EAAQ,KAAK;EAAI,cAAc;EAAI,eAAe,YAAY,EAAE,OAAO;CACjF;CACD,KAAK;EACH,SAAS;EAAW,UAAU;EAAI,YAAY;EAAK,OAAO,EAAE;EAC5D,YAAY;EAAQ,QAAQ;EAAQ,cAAc;EAClD,QAAQ;EAAW,YAAY;CAChC;CACD,WAAW;EAAE,OAAO,EAAE;EAAI,eAAe,YAAY,EAAE,QAAQ;CAAG;AACnE;MAGYC,eAAuC;CAClD,OAAO,EAAE;CACT,WAAW,EAAE;CACb,QAAQ,EAAE;AACX;MAGYC,sBAA8C;CACzD,WAAW,EAAE;CACb,OAAO,EAAE;CACT,SAAS,EAAE;AACZ;MAEYC,qBAA6C;CACxD,WAAW;CACX,OAAO;CACP,SAAS;AACV;MAGYC,aAAqC;CAChD,OAAO,EAAE;CACT,YAAY,EAAE;AACf;;;;ACnHD,MAAMC,cAAsC;CAAE,OAAO;CAAM,WAAW;CAAO,QAAQ;AAAM;AAEpF,SAAS,OAAOC,IAAoB;AACzC,MAAK,OAAO,SAAS,GAAG,IAAI,MAAM,EAAG,QAAO;CAC5C,MAAM,OAAO,KAAK,KAAK,GAAG;AAC1B,KAAI,OAAO,IAAQ,QAAO;AAC1B,KAAI,OAAO,KAAW,SAAQ,EAAE,KAAK,MAAM,OAAO,IAAO,CAAC;AAC1D,KAAI,OAAO,MAAY,SAAQ,EAAE,KAAK,MAAM,OAAO,KAAU,CAAC;AAC9D,SAAQ,EAAE,KAAK,MAAM,OAAO,MAAW,CAAC;AACzC;AAED,SAAS,SAASC,OAAsB,MAAM,IAAY;AACxD,KAAI,UAAU,KAAM,QAAO;AAC3B,QAAO,MAAM,SAAS,OAAO,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK;AACzD;AAaM,SAAS,aAAaC,OAAwC;CACnE,MAAM,EAAE,OAAO,MAAM,UAAU,OAAO,YAAY,SAAS,UAAU,cAAc,GAAG;CACtF,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,SAAS,CAAC;CAE3D,MAAM,OAAO,MAAM,WAAW,MAAM,UAEhC,2BAAC,MAAA,EAAA,UACC,2BAAC,MAAA;EAAG,OAAO;GAAE,GAAG,EAAE;GAAI,OAAO,EAAE;GAAK,SAAS;GAAY,WAAW;EAAU;EAAE,SAAS;EAAA,UAAG;CAAA,EAEvF,CAAA,EACF,GAEL,MAAM,IAAI,CAAA,SACV,4BAAC,MAAA;EAEC,OAAO;GAAE,GAAG,EAAE;GAAK,GAAI,KAAK,OAAO,aAAa,EAAE,cAAc;EAAO;EACvE,SAAS,MAAM,SAAS,KAAK;EAAA,UAAA;GAE7B,2BAAC,MAAA;IAAG,OAAO,EAAE;IAAI,OAAO,KAAK;IAAA,UAAO,SAAS,KAAK,MAAM,GAAG;GAAA,EAAM;GACjE,2BAAC,MAAA;IAAG,OAAO,EAAE;IAAI,OAAO,KAAK;IAAA,UAAW,SAAS,KAAK,UAAU,GAAG;GAAA,EAAM;GACzE,2BAAC,MAAA;IAAG,OAAO,EAAE;IAAA,UAAK,KAAK;GAAA,EAAc;GACrC,2BAAC,MAAA;IAAG,OAAO,EAAE;IAAA,UAAK,KAAK;GAAA,EAAiB;GACxC,4BAAC,MAAA;IAAG,OAAO,EAAE;IAAA,UAAA,CACX,2BAAC,QAAA,EAAK,OAAO;KAAE,GAAG,EAAE;KAAK,YAAY,aAAa,KAAK,WAAW,EAAE;IAAK,EAAA,EAAI,EAC5E,YAAY,KAAK,WAAW,KAAK,MAAA;GAAA,EAC/B;GACL,2BAAC,MAAA;IAAG,OAAO,EAAE;IAAI,OAAO,KAAK,cAAc;IAAA,UAAK,SAAS,KAAK,YAAY,GAAG;GAAA,EAAM;GACnF,2BAAC,MAAA;IAAG,OAAO;KAAE,GAAG,EAAE;KAAI,OAAO,EAAE;IAAK;IAAA,UAAG,OAAO,KAAK,UAAU;GAAA,EAAM;EAAA;CAAA,IAb7D,EAAE,KAAK,SAAS,GAAG,KAAK,GAAG,EAc9B,CACL;AAEJ,QACE,4BAAC,OAAA,EAAA,UAAA,CACC,2BAAC,OAAA;EAAI,OAAO,EAAE;EAAA,UACZ,4BAAC,SAAA;GAAM,OAAO,EAAE;GAAA,UAAA,CACd,2BAAC,SAAA,EAAA,UACC,4BAAC,MAAA,EAAA,UAAA;IACC,2BAAC,MAAA;KAAG,OAAO,EAAE;KAAA,UAAI;IAAA,EAAO;IACxB,2BAAC,MAAA;KAAG,OAAO,EAAE;KAAA,UAAI;IAAA,EAAQ;IACzB,2BAAC,MAAA;KAAG,OAAO,EAAE;KAAA,UAAI;IAAA,EAAO;IACxB,2BAAC,MAAA;KAAG,OAAO,EAAE;KAAA,UAAI;IAAA,EAAO;IACxB,2BAAC,MAAA;KAAG,OAAO,EAAE;KAAA,UAAI;IAAA,EAAO;IACxB,2BAAC,MAAA;KAAG,OAAO,EAAE;KAAA,UAAI;IAAA,EAAO;IACxB,2BAAC,MAAA;KAAG,OAAO,EAAE;KAAA,UAAI;IAAA,EAAS;GAAA,EAAA,EACvB,CAAA,EACC,EACR,2BAAC,SAAA,EAAA,UAAO,KAAA,EAAa;EAAA,EACf;CAAA,EACJ,EACN,4BAAC,OAAA;EAAI,OAAO;GAAE,GAAG,EAAE;GAAK,SAAS;GAAQ,YAAY;GAAU,KAAK;GAAG,WAAW;EAAG;EAAA,UAAA;GACnF,2BAAC,UAAA;IACC,MAAK;IACL,OAAO;KAAE,GAAG,EAAE;KAAQ,GAAI,QAAQ,IAAI,EAAE,iBAAiB;IAAO;IAChE,UAAU,QAAQ;IAClB,SAAS,MAAM,aAAa,EAAE;IAAA,UAC/B;GAAA,EAAa;GACd,2BAAC,UAAA;IACC,MAAK;IACL,OAAO;KAAE,GAAG,EAAE;KAAQ,GAAI,QAAQ,IAAI,EAAE,iBAAiB;IAAO;IAChE,UAAU,QAAQ;IAClB,SAAS,MAAM,aAAa,OAAO,EAAE;IAAA,UACtC;GAAA,EAAY;GACb,4BAAC,QAAA,EAAA,UAAA;IAAK;IAAG,KAAK,IAAI,MAAM,WAAW;IAAC;IAAI;IAAW;IAAQ;IAAM;GAAA,EAAA,EAAS;GAC1E,2BAAC,UAAA;IACC,MAAK;IACL,OAAO;KAAE,GAAG,EAAE;KAAQ,GAAI,QAAQ,aAAa,EAAE,iBAAiB;IAAO;IACzE,UAAU,QAAQ;IAClB,SAAS,MAAM,aAAa,OAAO,EAAE;IAAA,UACtC;GAAA,EAAY;GACZ,UAAU,2BAAC,QAAA;IAAK,OAAO,EAAE;IAAA,UAAO;GAAA,EAAW,GAAG;EAAA;CAAA,EAC3C,EAAA,EACF;AAET;;;;;ACxFD,MAAM,oBAAoB;;AAE1B,MAAM,YAAY;AAOlB,SAAS,aAAaC,QAA2B;AAC/C,MAAK,MAAM,QAAQ,OAAO,CAAE,QAAO;AACnC,QAAO,OACJ,MAAM,GAAG,wBAAwB,CACjC,IAAI,CAAA,UAAU,UAAU,OAAO,YAAY,OAAO,MAAM,CAAE,CAC1D,KAAK,IAAI;AACb;AAED,SAAS,aAAaC,MAAiC;AACrD,QAAO,KACJ,MAAM,kBAAkB,CACxB,IAAI,CAAA,UAAS,MAAM,MAAM,CAAC,CAC1B,OAAO,CAAA,UAAS,MAAM,SAAS,EAAE,CACjC,IAAI,CAAA,UAAU,UAAU,YAAY,OAAO,MAAO;AACtD;AAED,SAAS,cAAcC,SAAoE;CACzF,MAAMC,SAAsC,CAAE;AAC9C,MAAK,MAAM,UAAU,QACnB,QAAO,OAAO,iBAAiB;EAC7B,aAAa,OAAO,eAAe;EACnC,YAAY,aAAa,OAAO,OAAO;CACxC;AAEH,QAAO;AACR;AAUM,SAAS,cAAcC,OAAyC;CACrE,MAAM,EAAE,QAAQ,UAAU,WAAW,WAAW,SAAS,GAAG;CAC5D,MAAM,CAAC,QAAM,QAAQ,GAAG,oBAAS,OAAO,KAAK;CAC7C,MAAM,CAAC,aAAa,eAAe,GAAG,oBAAS,OAAO,eAAe,GAAG;CACxE,MAAM,CAAC,YAAY,cAAc,GAAG,oBAAS,OAAO,cAAc,GAAG;CACrE,MAAM,CAAC,cAAc,gBAAgB,GAAG,oBAAsC,MAAM,cAAc,OAAO,QAAQ,CAAC;CAClH,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAS,MAAM;CAC3C,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,MAAM;CAC/C,MAAM,CAAC,YAAY,cAAc,GAAG,oBAAS,MAAM;CAEnD,SAAS,kBAAkBC,KAAaC,OAAmC;AACzE,kBAAgB,CAAA,cAAa;GAAE,GAAG;IAAW,MAAM;IAAE,GAAG,SAAS;IAAM,GAAG;GAAO;EAAE,GAAE;CACtF;CAED,MAAM,cAAc,OAAO,QAAQ,KAAK,CAAA,WAAU;EAChD,MAAM,QAAQ,aAAa,OAAO;AAClC,MAAI,UAAU,UAAW,QAAO;AAChC,SAAO,MAAM,iBAAiB,OAAO,eAAe,OAAO,MAAM,eAAe,aAAa,OAAO,OAAO;CAC5G,EAAC;CACF,MAAM,QACJ,WAAS,OAAO,QAChB,iBAAiB,OAAO,eAAe,OACvC,gBAAgB,OAAO,cAAc,OACrC;;CAGF,SAAS,qBAA2C;EAClD,MAAMC,UAAgC,CAAE;AACxC,OAAK,MAAM,UAAU,OAAO,SAAS;GACnC,MAAM,QAAQ,aAAa,OAAO;AAClC,OAAI,UAAU,UAAW;GACzB,MAAMC,QAA4B,EAAE,eAAe,OAAO,cAAe;GACzE,IAAI,UAAU;AACd,OAAI,MAAM,iBAAiB,OAAO,eAAe,KAAK;AACpD,UAAM,cAAc,MAAM;AAC1B,cAAU;GACX;AACD,OAAI,MAAM,eAAe,aAAa,OAAO,OAAO,EAAE;AACpD,UAAM,SAAS,aAAa,MAAM,WAAW;AAC7C,cAAU;GACX;AACD,OAAI,QAAS,SAAQ,KAAK,MAAM;EACjC;AACD,SAAO;CACR;CAED,eAAe,aAA4B;AACzC,OAAK,SAAS,OAAQ;EACtB,MAAM,gBAAgB,oBAAoB;EAC1C,MAAMC,OAA4B;GAAE;GAAM;GAAa;EAAY;AACnE,MAAI,cAAc,SAAS,EAAG,MAAK,UAAU;AAC7C,YAAU,KAAK;AACf,MAAI;GACF,MAAM,UAAU,MAAM,aAAa,OAAO,IAAI,OAAO,UAAU,KAAK;AACpE,aAAU,QAAQ;EACnB,SAAQ,OAAO;AACd,WAAQ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,QAAQ;EAC5D,UAAS;AACR,aAAU,MAAM;EACjB;CACF;CAED,eAAe,eAA8B;AAC3C,MAAI,SAAU;AACd,cAAY,KAAK;AACjB,MAAI;AACF,SAAM,cAAc,OAAO,IAAI,OAAO,SAAS;AAC/C,aAAU,OAAO,GAAG;EACrB,SAAQ,OAAO;AACd,WAAQ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,QAAQ;AAC3D,iBAAc,MAAM;EACrB,UAAS;AACR,eAAY,MAAM;EACnB;CACF;CAED,MAAM,YAAY,YAAY;AAE9B,QACE,4BAAC,OAAA;EAAI,OAAO,EAAE;EAAA,UAAA;GACZ,4BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAM,SAAS;KAAQ,gBAAgB;KAAiB,cAAc;IAAI;IAAA,UAAA,CAC3F,2BAAC,UAAA;KAAO,OAAO,EAAE,UAAU,GAAI;KAAA,UAAE;IAAA,EAAc,EAC/C,4BAAC,QAAA;KAAK,OAAO,EAAE;KAAA,UAAA,CAAO,QAAK,OAAO,QAAA;IAAA,EAAgB;GAAA,EAC9C;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAU,EACjC,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAO,OAAO;MAAgB;MAAU,UAAU,CAAA,MAAK,QAAQ,EAAE,OAAO,MAAM;KAAA,EAAI;IAAA,EAC9F,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAY,EACnC,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAO,OAAO;MAAsB;MAAU,UAAU,CAAA,MAAK,cAAc,EAAE,OAAO,MAAM;KAAA,EAAI;IAAA,EAC1G;GAAA,EACF;GACN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,2BAAC,SAAA;KAAM,OAAO,EAAE;KAAA,UAAO;IAAA,EAAU,EACjC,2BAAC,YAAA;KAAS,OAAO,EAAE;KAAU,OAAO;KAAuB;KAAU,UAAU,CAAA,MAAK,eAAe,EAAE,OAAO,MAAM;IAAA,EAAI;GAAA,EAClH;GAEN,4BAAC,OAAA;IAAI,OAAO;KAAE,SAAS;KAAQ,KAAK;KAAG,YAAY;IAAU;IAAA,UAAA;KAC3D,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAe,GAAI,aAAa,QAAQ,EAAE,iBAAiB;MAAO;MAChF,UAAU,aAAa;MACvB,SAAS,WAAW,YAAY;MAAA,UAChC,SAAS,SAAS;KAAA,EAAgB;MAElC,aAEE,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAc,IAAK,WAAW,EAAE,iBAAiB;MAAO;MACtE,WAAW;MACX,SAAS,MAAM,cAAc,KAAK;MAAA,UACnC;KAAA,EAAc,GAGf,4BAAA,4BAAA,EAAA,UAAA;MACE,4BAAC,QAAA;OAAK,OAAO,EAAE,OAAO,EAAE,OAAQ;OAAA,UAAA;QAAE;QAAM,OAAO;QAAK;OAAA;MAAA,EAAe;MACnE,2BAAC,UAAA;OACC,MAAK;OACL,OAAO;QAAE,GAAG,EAAE;QAAc,GAAI,WAAW,EAAE,iBAAiB;OAAO;OACrE,UAAU;OACV,SAAS,WAAW,cAAc;OAAA,UAClC,WAAW,SAAS;MAAA,EAAgB;MACtC,2BAAC,UAAA;OAAO,MAAK;OAAS,OAAO,EAAE;OAAQ,UAAU;OAAU,SAAS,MAAM,cAAc,MAAM;OAAA,UAAE;MAAA,EAAW;KAAA,EAAA,EAC1G;MAEL,WAAW,2BAAC,QAAA;MAAK,OAAO,EAAE;MAAA,UAAO;KAAA,EAAwB,GAAG;IAAA;GAAA,EAC1D;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE,WAAW,GAAI;IAAA,UAAA,CAC3B,4BAAC,OAAA;KAAI,OAAO;MAAE,GAAG,EAAE;MAAO,cAAc;KAAG;KAAA,UAAA;MAAE;MAClC,OAAO,QAAQ;MAAO;KAAA;IAAA,EAC3B,EACN,2BAAC,OAAA;KAAI,OAAO;MAAE,GAAG,EAAE;MAAQ,WAAW;KAAK;KAAA,UACzC,4BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAA,CACd,2BAAC,SAAA,EAAA,UACC,4BAAC,MAAA,EAAA,UAAA;OACC,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;OACxB,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;OACxB,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;OACxB,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;OACxB,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;MAAA,EAAA,EACrB,CAAA,EACC,EACR,2BAAC,SAAA,EAAA,UACE,OAAO,QAAQ,IAAI,CAAA,WAClB,4BAAC,MAAA,EAAA,UAAA;OACC,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAI,OAAO,OAAO;QAAA,UAAO,OAAO;OAAA,EAAU;OACvD,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAK,yBAAyB,OAAO,SAAS,OAAO;OAAA,EAAU;OAC5E,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAK,OAAO,WAAW,MAAM;OAAA,EAAS;OACnD,2BAAC,MAAA;QAAG,OAAO;SAAE,GAAG,EAAE;SAAI,UAAU;QAAW;QAAA,UACzC,2BAAC,SAAA;SACC,OAAO,EAAE;SACT,OAAO,aAAa,OAAO,gBAAgB,eAAe;SAChD;SACV,aAAY;SACZ,UAAU,CAAA,UAAS,kBAAkB,OAAO,eAAe,EAAE,aAAa,MAAM,OAAO,MAAO,EAAC;QAAA,EAC/F;OAAA,EACC;OACL,2BAAC,MAAA;QAAG,OAAO;SAAE,GAAG,EAAE;SAAI,UAAU;QAAW;QAAA,UACzC,2BAAC,SAAA;SACC,OAAO,EAAE;SACT,OAAO,aAAa,OAAO,gBAAgB,cAAc;SAC/C;SACV,aAAY;SACZ,UAAU,CAAA,UAAS,kBAAkB,OAAO,eAAe,EAAE,YAAY,MAAM,OAAO,MAAO,EAAC;QAAA,EAC9F;OAAA,EACC;MAAA,EAAA,GArBE,OAAO,cAsBX,CACL,CAAA,EACI;KAAA,EACF;IAAA,EACJ;GAAA,EACF;EAAA;CAAA,EACF;AAET;;;;;AC7ND,MAAM,oBAAoB,CAAC,GAAG,IAAI,IAAI;CAAC;CAAqB;CAAI;AAAwB,EAAE,EAAC,KAAK,CAAC,GAAG,MAAM,IAAI,EAAE;;AAGhH,MAAM,kBAAkB;AAExB,SAAS,SAASC,OAAwB;AACxC,YAAW,UAAU,SAAU,QAAO;AACtC,YAAW,UAAU,YAAY,UAAU,KACzC,KAAI;AACF,SAAO,KAAK,UAAU,MAAM;CAC7B,QAAO;AACN,SAAO;CACR;AAEH,QAAO,OAAO,MAAM;AACrB;AAED,SAAS,KAAKC,OAAyC;CACrD,MAAM,EAAE,OAAO,GAAG;AAClB,KAAI,UAAU,QAAQ,UAAU,UAC9B,QAAO,2BAAC,QAAA;EAAK,OAAO,EAAE,OAAO,EAAE,IAAK;EAAA,UAAE;CAAA,EAAQ;CAEhD,MAAM,OAAO,SAAS,MAAM;AAC5B,QAAO,2BAAC,QAAA;EAAK,OAAO;EAAA,UAAO,KAAK,SAAS,mBAAmB,EAAE,KAAK,MAAM,GAAG,gBAAgB,CAAC,KAAK;CAAA,EAAY;AAC/G;AAWM,SAAS,UAAUC,OAAqC;CAC7D,MAAM,EAAE,WAAW,UAAU,cAAc,QAAQ,GAAG;CACtD,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAmC,KAAK;CAChE,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAS,EAAE;CACnC,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,oBAAoB;CAC7D,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAS,MAAM;CAC7C,MAAM,CAAC,OAAO,SAAS,GAAG,oBAAwB,KAAK;CACvD,MAAM,YAAY,kBAAO,EAAE;CAE3B,MAAM,OAAO,uBAAY,OAAOC,QAAgBC,SAAgC;EAC9E,MAAM,KAAK,UAAU,UAAU;AAC/B,YAAU,UAAU;AACpB,aAAW,KAAK;AAChB,WAAS,KAAK;AACd,MAAI;GACF,MAAM,SAAS,MAAM,gBAAgB,WAAW,UAAU,QAAQ,KAAK;AACvE,OAAI,OAAO,UAAU,QAAS;AAC9B,WAAQ,OAAO;AAEf,WAAQ,OAAO,KAAK;EACrB,SAAQ,SAAS;AAChB,OAAI,OAAO,UAAU,QAAS;AAC9B,YAAS,mBAAmB,QAAQ,QAAQ,UAAU,SAAS;EAChE,UAAS;AACR,OAAI,OAAO,UAAU,QAAS,YAAW,MAAM;EAChD;CACF,GAAE,CAAC,WAAW,QAAS,EAAC;AAGzB,sBAAU,MAAM;AACd,UAAQ,KAAK;AACb,OAAK,KAAK,GAAG,SAAS;CACvB,GAAE,CAAC,MAAM,QAAS,EAAC;CAEpB,MAAM,QAAQ,MAAM,SAAS;CAC7B,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,UAAU,MAAM,WAAW,CAAE;CACnC,MAAM,OAAO,MAAM,QAAQ,CAAE;AAE7B,QACE,4BAAC,OAAA;EAAI,OAAO,EAAE;EAAA,UAAA;GACZ,4BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAM,SAAS;KAAQ,gBAAgB;KAAiB,cAAc;IAAI;IAAA,UAAA,CAC3F,2BAAC,UAAA;KAAO,OAAO,EAAE,UAAU,GAAI;KAAA,UAAE;IAAA,EAAY,EAC7C,2BAAC,QAAA;KAAK,OAAO,EAAE;KAAA,UAAQ,UAAU,UAAU,IAAI,MAAM;IAAA,EAAW;GAAA,EAC5D;GAEL,WAAW,UAAU,4BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAO,cAAc;IAAG;IAAA,UAAA;KAAE;KAAU;KAAO;IAAA;GAAA,EAAe,GAAG;GACrG,UAAU,OAAO,2BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAQ,WAAW;IAAG;IAAA,UAAE,2BAAC,QAAA,EAAA,UAAM,MAAA,EAAa;GAAA,EAAM,GAAG;GACzF,iBAAiB,SAAS,UAAU,QAAQ,SAAS,OAClD,4BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAO,cAAc;IAAG;IAAA,UAAA;KAAE;KAAO;KAAa;KAAO;KAAM;IAAA;GAAA,EAAS,GACvF;GAEH,QAAQ,SAAS,IAEd,2BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAQ,WAAW;IAAK;IAAA,UACzC,4BAAC,SAAA;KAAM,OAAO,EAAE;KAAA,UAAA,CACd,2BAAC,SAAA,EAAA,UACC,4BAAC,MAAA,EAAA,UAAA,CACC,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAM,EACtB,QAAQ,IAAI,CAAA,WAAU,2BAAC,MAAA;MAAgB,OAAO,EAAE;MAAI,OAAO;MAAA,UAAS;KAAA,GAArC,OAAiD,CAAC,EAAA,EAC/E,CAAA,EACC,EACR,4BAAC,SAAA,EAAA,UAAA,CACE,KAAK,IAAI,CAAA,QACR,4BAAC,MAAA,EAAA,UAAA,CACC,2BAAC,MAAA;MAAG,OAAO;OAAE,GAAG,EAAE;OAAI,OAAO,EAAE;OAAK,UAAU;MAAI;MAAA,UAAG,OAAO,IAAI,wBAAwB,GAAG;KAAA,EAAM,EAChG,QAAQ,IAAI,CAAA,WACX,2BAAC,MAAA;MAAgB,OAAO,EAAE;MAAA,UAAI,2BAAC,MAAA,EAAK,OAAO,IAAI,QAAA,EAAW;KAAA,GAAjD,OAAsD,CAC/D,EAAA,GAJK,OAAO,IAAI,wBAAwB,GAAG,CAK1C,CACL,EACD,KAAK,WAAW,MAAM,UAEnB,2BAAC,MAAA,EAAA,UACC,2BAAC,MAAA;MAAG,OAAO;OAAE,GAAG,EAAE;OAAI,OAAO,EAAE;OAAK,SAAS;OAAY,WAAW;MAAU;MAAE,SAAS,QAAQ,SAAS;MAAA,UACvG,UAAU,IAAI,eAAe;KAAA,EAC3B,CAAA,EACF,GAEL,IAAA,EAAA,EACE;IAAA,EACF;GAAA,EACJ,GAEN,UACE,2BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAO;GAAA,EAAU,GAC/B,UAAU,OACR,2BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAO;GAAA,EAAyB,GAC9C;GAER,4BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAK,SAAS;KAAQ,YAAY;KAAU,KAAK;KAAG,WAAW;IAAG;IAAA,UAAA;KACnF,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAQ,GAAI,QAAQ,KAAK,UAAU,EAAE,iBAAiB;MAAO;MAC3E,UAAU,QAAQ,KAAK;MACvB,SAAS,WAAW,KAAK,GAAG,SAAS;MAAA,UACtC;KAAA,EAAa;KACd,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAQ,GAAI,QAAQ,KAAK,UAAU,EAAE,iBAAiB;MAAO;MAC3E,UAAU,QAAQ,KAAK;MACvB,SAAS,WAAW,KAAK,OAAO,GAAG,SAAS;MAAA,UAC7C;KAAA,EAAY;KACb,4BAAC,QAAA,EAAA,UAAA;MAAK;MAAG;MAAK;MAAI;MAAW;KAAA,EAAA,EAAS;KACtC,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAQ,GAAI,QAAQ,cAAc,UAAU,EAAE,iBAAiB;MAAO;MACpF,UAAU,QAAQ,cAAc;MAChC,SAAS,WAAW,KAAK,OAAO,GAAG,SAAS;MAAA,UAC7C;KAAA,EAAY;KACb,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAQ,GAAI,QAAQ,cAAc,UAAU,EAAE,iBAAiB;MAAO;MACpF,UAAU,QAAQ,cAAc;MAChC,SAAS,WAAW,KAAK,YAAY,SAAS;MAAA,UAC/C;KAAA,EAAa;KACd,4BAAC,SAAA;MAAM,OAAO;OAAE,GAAG,EAAE;OAAO,SAAS;OAAQ,YAAY;OAAU,KAAK;MAAG;MAAA,UAAA;OAAE;OAE3E,2BAAC,UAAA;QACC,OAAO,EAAE;QACT,OAAO;QACP,UAAU;QACV,UAAU,CAAA,UAAS,YAAY,OAAO,MAAM,OAAO,MAAM,CAAC;QAAA,UAEzD,kBAAkB,IAAI,CAAA,SAAQ,2BAAC,UAAA;SAAkB,OAAO;SAAA,UAAO;QAAA,GAApB,KAAkC,CAAC;OAAA,EACxE;OAAA;MAAA;KAAA,EAEH;KACR,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAQ,GAAI,UAAU,EAAE,iBAAiB;MAAO;MAC9D,UAAU;MACV,SAAS,WAAW,KAAK,MAAM,SAAS;MAAA,UACzC;KAAA,EAAW;IAAA;GAAA,EACR;EAAA;CAAA,EACF;AAET;;;;ACpKD,MAAMC,eAA4B;CAAE,MAAM;CAAI,MAAM;CAAQ,UAAU;CAAO,aAAa;AAAI;AAEvF,SAAS,WAAWC,OAAsC;CAC/D,MAAM,EAAE,QAAQ,WAAW,UAAU,SAAS,GAAG;CACjD,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,OAAO,IAAI,YAAY,GAAG;CACnE,MAAM,CAAC,QAAM,QAAQ,GAAG,oBAAS,GAAG;CACpC,MAAM,CAAC,aAAa,eAAe,GAAG,oBAAS,GAAG;CAClD,MAAM,CAAC,YAAY,cAAc,GAAG,oBAAS,GAAG;CAChD,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAwB,CAAC,EAAE,GAAG,aAAe,CAAA,EAAC;CAC5E,MAAM,CAAC,YAAY,cAAc,GAAG,oBAAS,MAAM;CAEnD,MAAM,YACJ,SAAS,SAAS,KAClB,OAAK,MAAM,CAAC,SAAS,KACrB,QAAQ,SAAS,KACjB,QAAQ,UAAU,qBAClB,QAAQ,MAAM,CAAA,WAAU,OAAO,KAAK,MAAM,CAAC,SAAS,EAAE;CAExD,SAAS,aAAaC,OAAeC,OAAmC;AACtE,aAAW,CAAA,aAAY,SAAS,IAAI,CAAC,QAAQ,MAAO,MAAM,QAAQ;GAAE,GAAG;GAAQ,GAAG;EAAO,IAAG,OAAQ,CAAC;CACtG;CAED,eAAe,eAA8B;AAC3C,OAAK,aAAa,WAAY;EAC9B,MAAM,OAAO;GACX;GACA,MAAM,OAAK,MAAM;GACjB,aAAa,YAAY,MAAM,CAAC,SAAS,IAAI,YAAY,MAAM,GAAG;GAClE,YAAY,WAAW,MAAM,CAAC,SAAS,IAAI,WAAW,MAAM,GAAG;GAC/D,SAAS,QAAQ,IAAgG,CAAA,YAAW;IAC1H,MAAM,OAAO,KAAK,MAAM;IACxB,MAAM,OAAO;IACb,UAAU,OAAO;IACjB,aAAa,OAAO,YAAY,MAAM,CAAC,SAAS,IAAI,OAAO,YAAY,MAAM,GAAG;GACjF,GAAE;EACJ;AACD,gBAAc,KAAK;AACnB,MAAI;GACF,MAAM,UAAU,MAAM,cAAc,KAAK;AACzC,aAAU,QAAQ;EACnB,SAAQ,OAAO;AACd,WAAQ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,QAAQ;EAC5D,UAAS;AACR,iBAAc,MAAM;EACrB;CACF;AAED,QACE,4BAAC,OAAA;EAAI,OAAO,EAAE;EAAA,UAAA;GACZ,4BAAC,OAAA;IAAI,OAAO;KAAE,SAAS;KAAQ,gBAAgB;KAAiB,cAAc;IAAI;IAAA,UAAA,CAChF,2BAAC,UAAA;KAAO,OAAO,EAAE,UAAU,GAAI;KAAA,UAAE;IAAA,EAAc,EAC/C,2BAAC,QAAA;KAAK,OAAO,EAAE;KAAA,UAAO;IAAA,EAA2B;GAAA,EAC7C;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAW,EAClC,2BAAC,UAAA;MAAO,OAAO,EAAE;MAAO,OAAO;MAAU,UAAU;MAAY,UAAU,CAAA,MAAK,YAAY,EAAE,OAAO,MAAM;MAAA,UACtG,OAAO,WAAW,IACf,2BAAC,UAAA;OAAO,OAAM;OAAA,UAAG;MAAA,EAAkB,GACnC,OAAO,IAAI,CAAA,UAAS,2BAAC,UAAA;OAA4B,OAAO,MAAM;OAAA,UAAW,MAAM;MAAA,GAA9C,MAAM,SAA0D,CAAC;KAAA,EAC/F;IAAA,EACL,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAU,EACjC,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAO,OAAO;MAAM,UAAU;MAAY,UAAU,CAAA,MAAK,QAAQ,EAAE,OAAO,MAAM;KAAA,EAAI;IAAA,EAChG;GAAA,EACF;GACN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAU,EACjC,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAO,OAAO;MAAa,UAAU;MAAY,UAAU,CAAA,MAAK,eAAe,EAAE,OAAO,MAAM;KAAA,EAAI;IAAA,EAC9G,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAgB,EACvC,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAO,OAAO;MAAY,UAAU;MAAY,UAAU,CAAA,MAAK,cAAc,EAAE,OAAO,MAAM;KAAA,EAAI;IAAA,EAC5G;GAAA,EACF;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA;KAAO;KAAK,QAAQ;KAAO;KAAI;KAAkB;IAAA;GAAA,EAAO;GACtE,2BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAQ,WAAW;KAAK,cAAc;IAAI;IAAA,UAC3D,4BAAC,SAAA;KAAM,OAAO,EAAE;KAAA,UAAA,CACd,2BAAC,SAAA,EAAA,UACC,4BAAC,MAAA,EAAA,UAAA;MACC,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;MACxB,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;MACxB,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;MACxB,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;MACxB,2BAAC,MAAA,EAAG,OAAO,EAAE,GAAA,EAAM;KAAA,EAAA,EAChB,CAAA,EACC,EACR,2BAAC,SAAA,EAAA,UACE,QAAQ,IAAI,CAAC,QAAQ,UACpB,4BAAC,MAAA,EAAA,UAAA;MACC,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UACX,2BAAC,SAAA;QACC,OAAO;SAAE,GAAG,EAAE;SAAO,QAAQ,OAAO,KAAK,MAAM,CAAC,WAAW,KAAK,YAAY,EAAE,OAAO,IAAI,EAAE,MAAM;QAAQ;QACzG,OAAO,OAAO;QACd,UAAU;QACV,UAAU,CAAA,MAAK,aAAa,OAAO,EAAE,MAAM,EAAE,OAAO,MAAO,EAAC;OAAA,EAC5D;MAAA,EACC;MACL,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UACX,2BAAC,UAAA;QACC,OAAO,EAAE;QACT,OAAO,OAAO;QACd,UAAU;QACV,UAAU,CAAA,MAAK,aAAa,OAAO,EAAE,MAAM,EAAE,OAAO,MAAO,EAAC;QAAA,UAE3D,mBAAmB,IAAI,CAAA,SACtB,2BAAC,UAAA;SAAkB,OAAO;SAAA,UAAO,yBAAyB;QAAA,GAA7C,KAA4D,CACzE;OAAA,EACK;MAAA,EACN;MACL,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UACX,2BAAC,SAAA;QACC,MAAK;QACL,SAAS,OAAO;QAChB,UAAU;QACV,UAAU,CAAA,MAAK,aAAa,OAAO,EAAE,UAAU,EAAE,OAAO,QAAS,EAAC;OAAA,EAClE;MAAA,EACC;MACL,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UACX,2BAAC,SAAA;QAAM,OAAO,EAAE;QAAO,OAAO,OAAO;QAAa,UAAU;QAAY,UAAU,CAAA,MAAK,aAAa,OAAO,EAAE,aAAa,EAAE,OAAO,MAAO,EAAC;OAAA,EAAI;MAAA,EAC5I;MACL,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UACX,2BAAC,QAAA;QACC,OAAO;SAAE,GAAG,EAAE;SAAM,GAAI,QAAQ,UAAU,KAAK,aAAa,EAAE,iBAAiB;QAAO;QACtF,SAAS,MAAM;AAAE,aAAI,QAAQ,SAAS,MAAM,WAAY,YAAW,CAAA,aAAY,SAAS,OAAO,CAAC,GAAG,MAAM,MAAM,MAAM,CAAC;QAAE;QAAA,UACzH;OAAA,EAAS;MAAA,EACP;KAAA,EAAA,GArCE,MAsCJ,CACL,CAAA,EACI;IAAA,EACF;GAAA,EACJ;GACN,2BAAC,UAAA;IACC,MAAK;IACL,OAAO;KAAE,GAAG,EAAE;KAAQ,GAAI,QAAQ,UAAU,qBAAqB,aAAa,EAAE,iBAAiB;IAAO;IACxG,UAAU,QAAQ,UAAU,qBAAqB;IACjD,SAAS,MAAM,WAAW,CAAA,aAAY,CAAC,GAAG,UAAU,EAAE,GAAG,aAAe,CAAA,EAAC;IAAA,UAC1E;GAAA,EAAa;GAEd,4BAAC,OAAA;IAAI,OAAO;KAAE,SAAS;KAAQ,KAAK;KAAG,WAAW;IAAI;IAAA,UAAA,CACpD,2BAAC,UAAA;KACC,MAAK;KACL,OAAO;MAAE,GAAG,EAAE;MAAe,IAAK,aAAa,aAAa,EAAE,iBAAiB;KAAO;KACtF,WAAW,aAAa;KACxB,SAAS,WAAW,cAAc;KAAA,UAClC,aAAa,SAAS;IAAA,EAAc,EACtC,2BAAC,UAAA;KAAO,MAAK;KAAS,OAAO,EAAE;KAAQ,UAAU;KAAY,SAAS;KAAA,UAAU;IAAA,EAAW;GAAA,EACvF;EAAA;CAAA,EACF;AAET;;;;AC/JM,SAAS,gBAA8B;CAC5C,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAsB,CAAE,EAAC;CACrD,MAAM,CAAC,OAAO,SAAS,GAAG,oBAA6B,CAAE,EAAC;CAC1D,MAAM,CAAC,OAAO,SAAS,GAAG,oBAAS,EAAE;CACrC,MAAM,CAAC,OAAO,SAAS,GAAG,oBAAS,GAAG;CACtC,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAS,EAAE;CACnC,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAe,SAAS;CAChD,MAAM,CAAC,YAAY,cAAc,GAAG,oBAAwB,KAAK;CACjE,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAmC,KAAK;CACpE,MAAM,CAAC,aAAa,eAAe,GAAG,oBAAS,MAAM;CACrD,MAAM,CAAC,eAAe,iBAAiB,GAAG,oBAAS,MAAM;CACzD,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,KAAK;CAC9C,MAAM,CAAC,OAAO,SAAS,GAAG,oBAAwB,KAAK;CACvD,MAAM,YAAY,kBAAO,EAAE;CAE3B,MAAM,aAAa,uBAAY,YAA2B;AACxD,MAAI;GACF,MAAM,SAAS,MAAM,YAAY;AACjC,aAAU,OAAO,OAAO;EACzB,SAAQ,SAAS;AAEhB,OAAI,mBAAmB,kBAAkB,QAAQ,SAAS,eAAe,QAAQ,SAAS,kBACxF,aAAY,MAAM;EAErB;CACF,GAAE,CAAE,EAAC;CAEN,MAAM,WAAW,uBAAY,OAAOC,GAAWC,MAA6B;EAC1E,MAAM,KAAK,UAAU,UAAU;AAC/B,YAAU,UAAU;AACpB,iBAAe,KAAK;AACpB,MAAI;GACF,MAAM,SAAS,MAAM,aAAa,GAAG,GAAG,gBAAgB;AACxD,OAAI,OAAO,UAAU,QAAS;AAC9B,YAAS,OAAO,MAAM;AACtB,YAAS,OAAO,MAAM;GACtB,MAAM,UAAU,OAAO,SAAS,IAAI,CAAA,OAAM,EAAE,EAAE,SAAS,IAAI,EAAE,QAAQ,EAAE,CAAC,KAAK,IAAI;AACjF,YAAS,QAAQ,SAAS,IAAI,UAAU,KAAK;EAC9C,SAAQ,SAAS;AAChB,OAAI,OAAO,UAAU,QAAS;AAC9B,YAAS,mBAAmB,QAAQ,QAAQ,UAAU,OAAO;EAC9D,UAAS;AACR,OAAI,OAAO,UAAU,QAAS,gBAAe,MAAM;EACpD;CACF,GAAE,CAAE,EAAC;AAGN,sBAAU,MAAM;AACd,OAAK,YAAY;AACjB,OAAK,SAAS,IAAI,EAAE;CACrB,GAAE,CAAC,YAAY,QAAS,EAAC;AAG1B,sBAAU,MAAM;EACd,MAAM,QAAQ,WAAW,MAAM;AAC7B,WAAQ,EAAE;AACV,QAAK,SAAS,OAAO,EAAE;EACxB,GAAE,IAAI;AACP,SAAO,MAAM,aAAa,MAAM;CACjC,GAAE,CAAC,OAAO,QAAS,EAAC;CAErB,MAAM,aAAa,uBAAY,OAAOC,SAA0C;AAC9E,gBAAc,KAAK,GAAG;AACtB,UAAQ,SAAS;AACjB,YAAU,KAAK;AACf,mBAAiB,KAAK;AACtB,MAAI;GACF,MAAM,UAAU,MAAM,WAAW,KAAK,IAAI,KAAK,SAAS;AACxD,aAAU,QAAQ;EACnB,SAAQ,SAAS;AAChB,YAAS,mBAAmB,QAAQ,QAAQ,UAAU,SAAS;EAChE,UAAS;AACR,oBAAiB,MAAM;EACxB;CACF,GAAE,CAAE,EAAC;CAEN,MAAM,mBAAmB,uBAAY,CAACC,YAAyB;AAC7D,WAAS,QAAQ,QAAQ;AACzB,MAAI,mBAAmB,kBAAkB,QAAQ,SAAS,eAAe,QAAQ,SAAS,kBACxF,aAAY,MAAM;CAErB,GAAE,CAAE,EAAC;CAEN,MAAM,gBAAgB,uBAAY,CAACC,YAAqC;AACtE,YAAU,QAAQ;AAClB,OAAK,SAAS,OAAO,KAAK;CAC3B,GAAE;EAAC;EAAO;EAAM;CAAS,EAAC;CAE3B,MAAM,gBAAgB,uBAAY,OAAOC,OAA8B;AACrE,gBAAc,KAAK;AACnB,YAAU,KAAK;AACf,QAAM,YAAY;AAClB,QAAM,SAAS,OAAO,KAAK;AAC3B,OAAK;CACN,GAAE;EAAC;EAAO;EAAM;EAAU;CAAW,EAAC;CAEvC,MAAM,gBAAgB,uBAAY,OAAOC,YAAoC;AAC3E,UAAQ,SAAS;AACjB,QAAM,YAAY;AAClB,QAAM,SAAS,OAAO,KAAK;EAC3B,MAAM,SAAS;AACf,gBAAc,OAAO,GAAG;AACxB,YAAU,OAAO;CAClB,GAAE;EAAC;EAAO;EAAM;EAAU;CAAW,EAAC;AAEvC,QACE,4BAAC,OAAA,EAAA,UAAA;EACC,4BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UAAA;IACZ,2BAAC,SAAA;KACC,OAAO,EAAE;KACT,aAAY;KACZ,OAAO;KACP,UAAU,CAAA,MAAK,SAAS,EAAE,OAAO,MAAM;IAAA,EACvC;IACF,2BAAC,UAAA;KACC,MAAK;KACL,OAAO;MAAE,GAAG,EAAE;MAAe,IAAK,WAAW,EAAE,iBAAiB;KAAO;KACvE,WAAW;KACX,SAAS,MAAM,QAAQ,SAAS;KAChC,OAAO,WAAW,WAAW;KAAA,UAC9B;IAAA,EAAgB;IACjB,2BAAC,UAAA;KAAO,MAAK;KAAS,OAAO,EAAE;KAAQ,UAAU;KAAa,SAAS,WAAW,SAAS,OAAO,KAAK;KAAA,UAAE;IAAA,EAAW;GAAA;EAAA,EAChH;EAEL,UAAU,OAEP,4BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UAAA,CACZ,2BAAC,QAAA,EAAA,UAAM,MAAA,EAAa,EACpB,2BAAC,QAAA;IAAK,OAAO;KAAE,GAAG,EAAE;KAAM,YAAY;IAAQ;IAAE,SAAS,MAAM,SAAS,KAAK;IAAA,UAAE;GAAA,EAAS;EAAA,EACpF,GAEN;EAEJ,2BAAC,cAAA;GACQ;GACD;GACN,UAAU;GACH;GACK;GACZ,SAAS;GACT,UAAU,CAAA,cAAa,WAAW,KAAK;GACvC,cAAc,CAAA,MAAK;AAAE,YAAQ,EAAE;AAAE,SAAK,SAAS,OAAO,EAAE;GAAE;EAAA,EAC1D;EAED,SAAS,WAEN,2BAAC,YAAA;GACS;GACR,WAAW,CAAA,kBAAe,cAAc,SAAO;GAC/C,UAAU,MAAM,QAAQ,SAAS;GACjC,SAAS;EAAA,EACT,GAEF,WAAW,OAET,4BAAA,4BAAA,EAAA,UAAA,CACE,2BAAC,eAAA;GACS;GACE;GACV,WAAW;GACX,WAAW,CAAA,YAAW,cAAc,GAAG;GACvC,SAAS;EAAA,EACT,EAEF,2BAAC,WAAA;GAEC,WAAW,OAAO;GAClB,UAAU,OAAO;GACjB,cAAc,OAAO;GACrB,QAAQ,OAAO;EAAA,IAJT,EAAE,OAAO,SAAS,GAAG,OAAO,GAAG,EAKrC,EAAA,EACD,GAGH,2BAAC,OAAA;GAAI,OAAO;IAAE,GAAG,EAAE;IAAO,WAAW;GAAI;GAAA,UACtC,gBAAgB,WAAW;EAAA,EACxB;CAAA,EAAA,EAER;AAET;;;;ACnKM,SAAS,eAAeC,OAA0C;CACvE,MAAM,EAAE,SAAS,SAAS,UAAU,SAAS,GAAG;CAChD,MAAM,UAAU,YAAY,QAAQ,YAAY;CAChD,MAAM,cAAc,SAAS,QAAQ;CAErC,MAAM,CAAC,QAAM,QAAQ,GAAG,oBAAS,SAAS,QAAQ,GAAG;CACrD,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAS,YAAY;CAC7C,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAS,SAAS,QAAQ,YAAY;CAC9D,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAS,OAAO,SAAS,QAAQ,2BAA2B,aAAa,CAAC;CAClG,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,SAAS,YAAY,GAAG;CACjE,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,SAAS,YAAY,GAAG;CACjE,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,GAAG;CAC5C,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAS,SAAS,WAAW,GAAG;CAC9D,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAS,SAAS,YAAY,OAAO,KAAK,OAAO,SAAS,WAAW,GAAG,CAAC;CACvG,MAAM,CAAC,aAAa,eAAe,GAAG,oBAAS,SAAS,eAAe,GAAG;CAC1E,MAAM,CAAC,WAAW,aAAa,GAAG,oBAAoB,OAAO;CAC7D,MAAM,CAAC,YAAY,cAAc,GAAG,oBAAoC,KAAK;CAC7E,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAS,MAAM;CAE3C,MAAM,iBACJ,OAAK,MAAM,CAAC,SAAS,KAAK,KAAK,MAAM,CAAC,SAAS,KAAK,SAAS,MAAM,CAAC,SAAS,KAAK,SAAS,MAAM,CAAC,SAAS;;CAG7G,SAAS,iBAAiBC,MAAwB;AAChD,QAAM;AACN,eAAa,OAAO;AACpB,gBAAc,KAAK;CACpB;CAED,SAAS,QAA0B;EACjC,MAAM,aAAa,OAAO,SAAS,MAAM,GAAG;EAC5C,MAAM,aAAa,OAAO,SAAS,SAAS,GAAG;AAC/C,SAAO;GACL;GACA,MAAM,KAAK,MAAM;GACjB,MAAM,OAAO,SAAS,WAAW,GAAG,aAAa,2BAA2B;GAC5E,UAAU,SAAS,MAAM;GACzB,UAAU,SAAS,MAAM;GACzB;GACA,SAAS,QAAQ,MAAM,CAAC,SAAS,IAAI,QAAQ,MAAM,GAAG;GACtD,SAAS,OAAO,SAAS,WAAW,GAAG,aAAa;EACrD;CACF;CAED,eAAe,aAA4B;AACzC,OAAK,kBAAkB,cAAc,UAAW;AAChD,eAAa,UAAU;AACvB,MAAI;GACF,MAAM,SAAS,MAAM,eAAe,OAAO,CAAC;AAC5C,iBAAc,OAAO;AACrB,gBAAa,OAAO,UAAU,OAAO,OAAO;EAC7C,SAAQ,OAAO;AACd,gBAAa,OAAO;AACpB,WAAQ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,UAAU;EAC9D;CACF;CAED,eAAe,aAA4B;AACzC,MAAI,cAAc,QAAQ,OAAQ;AAClC,YAAU,KAAK;AACf,MAAI;GACF,MAAM,aAAa,OAAO;GAC1B,MAAM,QAAQ,UACV,MAAM,gBAAgB,QAAQ,IAAI;IAClC,MAAM,OAAK,MAAM;IACjB,GAAG;IAEH,GAAI,SAAS,SAAS,IAAI,CAAE,IAAG,EAAE,UAAU,UAAW;IACtD,aAAa,YAAY,MAAM,CAAC,SAAS,IAAI,YAAY,MAAM,GAAG;IAClE,MAAM;GACP,EAAC,GACA,MAAM,iBAAiB;IACvB,GAAG;IACH,MAAM,OAAK,MAAM;IACjB,aAAa,YAAY,MAAM,CAAC,SAAS,IAAI,YAAY,MAAM,GAAG;IAClE,MAAM;GACP,EAAC;AACJ,WAAQ,MAAM;EACf,SAAQ,OAAO;AACd,WAAQ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,QAAQ;EAC5D,UAAS;AACR,aAAU,MAAM;EACjB;CACF;CAED,MAAM,WAAW,cAAc,QAC1B,OAAO,YAAY,WAAW,EAAE,IAAI,YAAY,YAAY,OAAO,MAAM,KAAK,YAAY,QAAQ,EAAE,KACrG,cAAc,SACX,YAAY,SAAS,SACtB;AAEN,QACE,4BAAC,OAAA;EAAI,OAAO,EAAE;EAAA,UAAA;GACZ,4BAAC,OAAA;IAAI,OAAO;KAAE,SAAS;KAAQ,gBAAgB;KAAiB,cAAc;IAAI;IAAA,UAAA,CAChF,2BAAC,UAAA;KAAO,OAAO,EAAE,UAAU,GAAI;KAAA,UAAG,UAAU,UAAU;IAAA,EAAiB,EACvE,2BAAC,QAAA;KAAK,OAAO,EAAE;KAAA,UACZ,WAAW,QAAQ,cAAc,iBAAiB;IAAA,EAC9C;GAAA,EACH;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAU,EACjC,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAO,OAAO;MAAM,UAAU;MAAQ,UAAU,CAAA,MAAK,QAAQ,EAAE,OAAO,MAAM;KAAA,EAAI;IAAA,EAC5F,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAU,EACjC,2BAAC,UAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,UAAU;MACV,UAAU,CAAA,MAAK,iBAAiB,MAAM;AACpC,eAAQ,EAAE,OAAO,MAAM;AACvB,eAAQ,OAAO,2BAA2B,EAAE,OAAO,OAAO,CAAC;MAC5D,EAAC;MAAA,UAED,mBAAmB,IAAI,CAAA,SACtB,2BAAC,UAAA;OAAkB,OAAO;OAAA,UAAO,yBAAyB;MAAA,GAA7C,KAA4D,CACzE;KAAA,EACK;IAAA,EACL;GAAA,EACF;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAU,EACjC,2BAAC,SAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,UAAU;MACV,UAAU,CAAA,MAAK,iBAAiB,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;KAAA,EAC9D;IAAA,EACE,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAU,EACjC,2BAAC,SAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,WAAU;MACV,UAAU;MACV,UAAU,CAAA,MAAK,iBAAiB,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;KAAA,EAC9D;IAAA,EACE;GAAA,EACF;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAY,EACnC,2BAAC,SAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,UAAU;MACV,UAAU,CAAA,MAAK,iBAAiB,MAAM,YAAY,EAAE,OAAO,MAAM,CAAC;KAAA,EAClE;IAAA,EACE,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAW,EAClC,2BAAC,SAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,UAAU;MACV,UAAU,CAAA,MAAK,iBAAiB,MAAM,YAAY,EAAE,OAAO,MAAM,CAAC;KAAA,EAClE;IAAA,EACE;GAAA,EACF;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAQ,UAAU,eAAe;KAAA,EAAa,EAC9D,2BAAC,SAAA;MACC,OAAO,EAAE;MACT,MAAK;MACL,OAAO;MACP,UAAU;MACV,UAAU,CAAA,MAAK,iBAAiB,MAAM,YAAY,EAAE,OAAO,MAAM,CAAC;KAAA,EAClE;IAAA,EACE,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAkB,EACzC,2BAAC,SAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,UAAU;MACV,aAAa,SAAS,UAAU,oCAAoC;MACpE,UAAU,CAAA,MAAK,iBAAiB,MAAM,WAAW,EAAE,OAAO,MAAM,CAAC;KAAA,EACjE;IAAA,EACE;GAAA,EACF;GAEN,4BAAC,OAAA;IAAI,OAAO,EAAE;IAAA,UAAA,CACZ,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAiB,EACxC,2BAAC,SAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,WAAU;MACV,UAAU;MACV,UAAU,CAAA,MAAK,iBAAiB,MAAM,WAAW,EAAE,OAAO,MAAM,CAAC;KAAA,EACjE;IAAA,EACE,EACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA,CACZ,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAO;KAAA,EAAc,EACrC,2BAAC,SAAA;MAAM,OAAO,EAAE;MAAO,OAAO;MAAa,UAAU;MAAQ,UAAU,CAAA,MAAK,eAAe,EAAE,OAAO,MAAM;KAAA,EAAI;IAAA,EAC1G;GAAA,EACF;GAEN,2BAAC,OAAA;IACC,OAAO;KACL,GAAG,EAAE;KACL,cAAc;KACd,aAAa,cAAc,OAAO,EAAE,KAAK,cAAc,SAAS,EAAE,SAAS,EAAE;KAC7E,OAAO,cAAc,OAAO,EAAE,KAAK,cAAc,SAAS,EAAE,SAAS,EAAE;IACxE;IAAA,UAED,2BAAC,QAAA,EAAA,UAAM,SAAA,EAAgB;GAAA,EACnB;GAEN,4BAAC,OAAA;IAAI,OAAO;KAAE,SAAS;KAAQ,KAAK;IAAG;IAAA,UAAA;KACrC,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAQ,IAAK,kBAAkB,cAAc,YAAY,EAAE,iBAAiB;MAAO;MACjG,WAAW,kBAAkB,cAAc;MAC3C,SAAS,WAAW,YAAY;MAAA,UAChC,cAAc,YAAY,SAAS;KAAA,EAAgB;KACrD,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAe,GAAI,cAAc,QAAQ,SAAS,EAAE,iBAAiB;MAAO;MAC1F,UAAU,cAAc,QAAQ;MAChC,SAAS,WAAW,YAAY;MAAA,UAChC,SAAS,SAAS;KAAA,EAAc;KAClC,2BAAC,UAAA;MAAO,MAAK;MAAS,OAAO,EAAE;MAAQ,UAAU;MAAQ,SAAS;MAAA,UAAU;KAAA,EAAW;IAAA;GAAA,EACnF;EAAA;CAAA,EACF;AAET;;;;AC5PD,SAAS,WAAWC,OAAuB;AACzC,KAAI,QAAQ,EAAG,QAAO;AACtB,KAAI,SAAS,IAAW,SAAQ,EAAE,CAAC,QAAQ,KAAW,QAAQ,EAAE,CAAC;AACjE,KAAI,SAAS,IAAO,SAAQ,EAAE,CAAC,QAAQ,KAAO,QAAQ,EAAE,CAAC;AACzD,QAAO,OAAO,MAAM;AACrB;AAEM,SAAS,kBAAkBC,OAA6C;CAC7E,MAAM,EAAE,QAAQ,QAAQ,SAAS,GAAG;CACpC,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAmB,CAAE,EAAC;CACpD,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAS,GAAG;CACxC,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAS,GAAG;CAC1C,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAA4B,CAAE,EAAC;CAC3D,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAS,MAAM;CAC7C,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAwB,KAAK;CAC7D,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,GAAG;CAC5C,MAAM,CAAC,QAAM,QAAQ,GAAG,oBAAS,GAAG;CACpC,MAAM,CAAC,WAAW,aAAa,GAAG,oBAAS,MAAM;CACjD,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAyC,KAAK;CAC1E,MAAM,YAAY,kBAAO,EAAE;CAE3B,MAAM,OAAO,uBAAY,OAAOC,WAAmBC,MAA6B;EAC9E,MAAM,KAAK,UAAU,UAAU;AAC/B,YAAU,UAAU;AACpB,aAAW,KAAK;AAChB,MAAI;GACF,MAAM,SAAS,MAAM,iBAAiB,OAAO,IAAI,WAAW,EAAE;AAC9D,OAAI,OAAO,UAAU,QAAS;AAC9B,cAAW,OAAO,QAAQ;AAC1B,aAAU,OAAO,OAAO;AACxB,eAAY,KAAK;EAClB,SAAQ,SAAS;AAChB,OAAI,OAAO,UAAU,QAAS;AAC9B,WAAQ,mBAAmB,QAAQ,UAAU,IAAI,MAAM,WAAW;EACnE,UAAS;AACR,OAAI,OAAO,UAAU,QAAS,YAAW,MAAM;EAChD;CACF,GAAE,CAAC,OAAO,IAAI,OAAQ,EAAC;AAGxB,sBAAU,MAAM;EACd,MAAM,QAAQ,WAAW,WAAW,KAAK,QAAQ,QAAQ,EAAE,QAAQ,MAAM,CAAC,SAAS,IAAI,MAAM,EAAE;AAC/F,SAAO,MAAM,aAAa,MAAM;CACjC,GAAE;EAAC;EAAM;EAAQ;CAAQ,EAAC;AAE3B,sBAAU,MAAM;AACd,MAAI,SAAS,WAAW,KAAK,OAAO,SAAS,EAAG,aAAY,OAAO,GAAG,SAAS;CAChF,GAAE,CAAC,QAAQ,QAAS,EAAC;CAEtB,MAAM,UAAU,OAAO,KAAK,CAAA,UAAS,MAAM,cAAc,SAAS,IAAI;CAEtE,eAAe,eAA8B;AAC3C,MAAI,aAAa,QAAQ,SAAS,WAAW,KAAK,UAAW;AAC7D,eAAa,KAAK;AAClB,YAAU,KAAK;AACf,MAAI;GACF,MAAM,WAAW,MAAM,kBAAkB,OAAO,IAAI;IAClD;IACA,WAAW;IACX,YAAY,OAAO,MAAM,CAAC,SAAS,IAAI,OAAO,MAAM,GAAG;IACvD,MAAM,OAAK,MAAM,CAAC,SAAS,IAAI,OAAK,MAAM,GAAG;GAC9C,EAAC;AACF,aAAU,SAAS;EACpB,SAAQ,SAAS;AAChB,WAAQ,mBAAmB,QAAQ,UAAU,IAAI,MAAM,QAAQ;EAChE,UAAS;AACR,gBAAa,MAAM;EACpB;CACF;AAED,QACE,4BAAC,OAAA;EAAI,OAAO;GAAE,GAAG,EAAE;GAAO,YAAY;GAAe,YAAY,YAAY,EAAE,OAAO;GAAG,cAAc;EAAG;EAAA,UAAA;GACxG,4BAAC,OAAA;IAAI,OAAO;KAAE,SAAS;KAAQ,gBAAgB;KAAiB,YAAY;KAAU,cAAc;IAAG;IAAA,UAAA,CACrG,2BAAC,UAAA;KAAO,OAAO,EAAE,UAAU,GAAI;KAAA,UAAE;IAAA,EAAY,EAC7C,2BAAC,QAAA;KAAK,OAAO,EAAE;KAAA,UAAQ,UAAU,UAAU,IAAI,OAAO,OAAO;IAAA,EAAW;GAAA,EACpE;GAEN,4BAAC,OAAA;IAAI,OAAO;KAAE,SAAS;KAAQ,KAAK;KAAG,cAAc;KAAI,UAAU;IAAQ;IAAA,UAAA;KACzE,2BAAC,UAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,UAAU,WAAW,QAAQ,UAAU;MACvC,UAAU,CAAA,MAAK,UAAU,EAAE,OAAO,MAAM;MAAA,UAEvC,QAAQ,WAAW,IAChB,2BAAC,UAAA;OAAO,OAAM;OAAA,UAAG;MAAA,EAAa,GAC9B,QAAQ,IAAI,CAAA,SAAQ,2BAAC,UAAA;OAAkB,OAAO;OAAA,UAAO;MAAA,GAApB,KAAkC,CAAC;KAAA,EACjE;KACT,2BAAC,SAAA;MACC,OAAO;OAAE,GAAG,EAAE;OAAQ,MAAM;OAAG,UAAU;MAAK;MAC9C,aAAY;MACZ,OAAO;MACP,UAAU,CAAA,MAAK,WAAW,EAAE,OAAO,MAAM;KAAA,EACzC;KACF,2BAAC,UAAA;MAAO,MAAK;MAAS,OAAO,EAAE;MAAQ,UAAU;MAAS,SAAS,WAAW,KAAK,QAAQ,QAAQ;MAAA,UAAE;KAAA,EAAW;IAAA;GAAA,EAC5G;GAEN,2BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAQ,WAAW;IAAK;IAAA,UACzC,4BAAC,SAAA;KAAM,OAAO,EAAE;KAAA,UAAA,CACd,2BAAC,SAAA,EAAA,UACC,4BAAC,MAAA,EAAA,UAAA;MACC,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;MACxB,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;MACxB,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;MACxB,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAI;MAAA,EAAO;KAAA,EAAA,EACrB,CAAA,EACC,EACR,2BAAC,SAAA,EAAA,UACE,OAAO,WAAW,MAAM,UAErB,2BAAC,MAAA,EAAA,UACC,2BAAC,MAAA;MAAG,OAAO;OAAE,GAAG,EAAE;OAAI,OAAO,EAAE;OAAK,WAAW;OAAU,SAAS;MAAY;MAAE,SAAS;MAAA,UAAG;KAAA,EAEvF,CAAA,EACF,GAEL,OAAO,IAAI,CAAA,UACX,4BAAC,MAAA;MAEC,OAAO;OAAE,GAAG,EAAE;OAAK,GAAI,MAAM,cAAc,WAAW,EAAE,cAAc;MAAO;MAC7E,SAAS,MAAM,YAAY,MAAM,cAAc,WAAW,OAAO,MAAM,UAAU;MAAA,UAAA;OAEjF,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAI,OAAO,MAAM;QAAA,UAAY,MAAM;OAAA,EAAe;OAC/D,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAK,WAAW,MAAM,SAAS;OAAA,EAAM;OAClD,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAK,MAAM,QAAQ;OAAA,EAAY;OAC5C,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAK,MAAM,cAAc;OAAA,EAAS;MAAA;KAAA,GAP1C,MAAM,UAQR,CACL,CAAA,EACE;IAAA,EACF;GAAA,EACJ;GAEL,YAAY,OAET,4BAAC,OAAA;IAAI,OAAO,EAAE,WAAW,GAAI;IAAA,UAAA,CAC3B,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA;MAAQ,QAAQ;MAAU;MAAO,QAAQ,QAAQ;MAAO;KAAA;IAAA,EAAO,EAC7E,2BAAC,OAAA;KAAI,OAAO;MAAE,GAAG,EAAE;MAAQ,WAAW;KAAK;KAAA,UACzC,4BAAC,SAAA;MAAM,OAAO,EAAE;MAAA,UAAA,CACd,2BAAC,SAAA,EAAA,UACC,4BAAC,MAAA,EAAA,UAAA;OACC,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;OACxB,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;OACxB,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;OACxB,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAI;OAAA,EAAO;MAAA,EAAA,EACrB,CAAA,EACC,EACR,2BAAC,SAAA,EAAA,UACE,QAAQ,QAAQ,IAAI,CAAA,WACnB,4BAAC,MAAA,EAAA,UAAA;OACC,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAI,OAAO,OAAO;QAAA,UAAO,OAAO;OAAA,EAAU;OACvD,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAK,yBAAyB,OAAO,SAAS,OAAO;OAAA,EAAU;OAC5E,2BAAC,MAAA;QAAG,OAAO,EAAE;QAAA,UAAK,OAAO,WAAW,MAAM;OAAA,EAAS;OACnD,2BAAC,MAAA;QAAG,OAAO;SAAE,GAAG,EAAE;SAAI,OAAO,EAAE;QAAK;QAAE,OAAO,OAAO,eAAe;QAAA,UAAK,OAAO,eAAe;OAAA,EAAS;MAAA,EAAA,GAJhG,OAAO,KAKX,CACL,CAAA,EACI;KAAA,EACF;IAAA,EACJ;GAAA,EACF,GAEN;GAEJ,4BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAK,WAAW;IAAI;IAAA,UAAA;KACrC,2BAAC,UAAA;MACC,OAAO,EAAE;MACT,OAAO;MACP,UAAU,aAAa,aAAa;MACpC,UAAU,CAAA,MAAK,YAAY,EAAE,OAAO,MAAM;MAAA,UAEzC,OAAO,WAAW,IACf,2BAAC,UAAA;OAAO,OAAM;OAAA,UAAG;MAAA,EAAkB,GACnC,OAAO,IAAI,CAAA,UAAS,2BAAC,UAAA;OAA4B,OAAO,MAAM;OAAA,UAAW,MAAM;MAAA,GAA9C,MAAM,SAA0D,CAAC;KAAA,EAC/F;KACT,2BAAC,SAAA;MACC,OAAO;OAAE,GAAG,EAAE;OAAQ,MAAM;OAAG,UAAU;MAAK;MAC9C,aAAY;MACZ,OAAO;MACP,UAAU;MACV,UAAU,CAAA,MAAK,QAAQ,EAAE,OAAO,MAAM;KAAA,EACtC;KACF,2BAAC,UAAA;MACC,MAAK;MACL,OAAO;OAAE,GAAG,EAAE;OAAe,GAAI,aAAa,QAAQ,SAAS,WAAW,KAAK,YAAY,EAAE,iBAAiB;MAAO;MACrH,UAAU,aAAa,QAAQ,SAAS,WAAW,KAAK;MACxD,SAAS,WAAW,cAAc;MAAA,UAClC,YAAY,SAAS;KAAA,EAAkB;IAAA;GAAA,EACrC;GAEL,WAAW,OAER,2BAAC,OAAA;IAAI,OAAO;KAAE,GAAG,EAAE;KAAQ,WAAW;KAAG,cAAc;KAAG,aAAa,EAAE;KAAI,OAAO,EAAE;IAAI;IAAA,UACxF,4BAAC,QAAA,EAAA,UAAA;KAAK;KACC,OAAO;KAAK;KAAa,OAAO;KAAU;KAAG,OAAO;KAAS;KAAM,OAAO;KAAY;KAC1F,OAAO,WAAW,YAAY,mBAAmB;IAAA,EAAA,EAC7C;GAAA,EACH,GAEN;EAAA;CAAA,EACA;AAET;;;;ACvMD,MAAM,kBAAkB;CAAC;CAAa;CAAkB;AAAkB;AAEnE,SAAS,mBAAiC;CAC/C,MAAM,CAAC,OAAO,SAAS,GAAG,oBAA2B,CAAE,EAAC;CACxD,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAsB,CAAE,EAAC;CACrD,MAAM,CAAC,OAAO,SAAS,GAAG,oBAAS,GAAG;CACtC,MAAM,CAAC,YAAY,cAAc,GAAG,oBAAwB,KAAK;CACjE,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAe,SAAS;CAChD,MAAM,CAAC,SAAS,WAAW,GAAG,oBAAS,MAAM;CAC7C,MAAM,CAAC,UAAU,YAAY,GAAG,oBAAS,KAAK;CAC9C,MAAM,CAAC,OAAO,SAAS,GAAG,oBAAwB,KAAK;CACvD,MAAM,CAAC,QAAQ,UAAU,GAAG,oBAAwB,KAAK;CACzD,MAAM,CAAC,WAAW,aAAa,GAAG,oBAAwB,KAAK;CAC/D,MAAM,CAAC,WAAW,aAAa,GAAG,oBAAwB,KAAK;CAE/D,MAAM,aAAa,uBAAY,YAA2B;AACxD,MAAI;AACF,cAAW,MAAM,YAAY,EAAE,OAAO;EACvC,QAAO,CAEP;CACF,GAAE,CAAE,EAAC;CAEN,MAAM,WAAW,uBAAY,YAA2B;AACtD,aAAW,KAAK;AAChB,MAAI;AACF,aAAU,MAAM,iBAAiB,EAAE,QAAQ;AAC3C,YAAS,KAAK;EACf,SAAQ,SAAS;AAChB,YAAS,mBAAmB,QAAQ,QAAQ,UAAU,UAAU;AAChE,OAAI,mBAAmB,iBAAiB,gBAAgB,SAAS,QAAQ,KAAK,CAAE,aAAY,MAAM;EACnG,UAAS;AACR,cAAW,MAAM;EAClB;CACF,GAAE,CAAE,EAAC;AAEN,sBAAU,MAAM;AACd,OAAK,YAAY;AACjB,OAAK,UAAU;CAChB,GAAE,CAAC,YAAY,QAAS,EAAC;CAE1B,MAAM,WAAW,MAAM,KAAK,CAAA,SAAQ,KAAK,OAAO,WAAW,IAAI;CAC/D,MAAM,SAAS,MAAM,MAAM,CAAC,aAAa;CACzC,MAAM,UAAU,OAAO,WAAW,IAC9B,QACA,MAAM,OAAO,CAAA,SACb,KAAK,KAAK,aAAa,CAAC,SAAS,OAAO,IACrC,KAAK,KAAK,aAAa,CAAC,SAAS,OAAO,IACxC,KAAK,KAAK,aAAa,CAAC,SAAS,OAAO,IACxC,KAAK,SAAS,aAAa,CAAC,SAAS,OAAO,CAC/C;CAEJ,eAAe,WAAWC,IAA2B;AACnD,MAAI,cAAc,KAAM;AACxB,eAAa,GAAG;AAChB,YAAU,KAAK;AACf,MAAI;GACF,MAAM,SAAS,MAAM,eAAe,EAAE,QAAQ,GAAI,EAAC;AACnD,aAAU,OAAO,WACZ,OAAO,OAAO,QAAQ,IAAI,OAAO,YAAY,OAAO,MAAM,KAAK,OAAO,QAAQ,EAAE,MAChF,OAAO,OAAO,SAAS,OAAO,EAAE;AACrC,SAAM,UAAU;EACjB,SAAQ,SAAS;AAChB,YAAS,mBAAmB,QAAQ,QAAQ,UAAU,SAAS;EAChE,UAAS;AACR,gBAAa,KAAK;EACnB;CACF;CAED,eAAe,aAAaA,IAA2B;AACrD,eAAa,KAAK;AAClB,MAAI;AACF,SAAM,iBAAiB,GAAG;AAC1B,OAAI,eAAe,IAAI;AACrB,kBAAc,KAAK;AACnB,YAAQ,SAAS;GAClB;AACD,SAAM,UAAU;EACjB,SAAQ,SAAS;AAChB,YAAS,mBAAmB,QAAQ,QAAQ,UAAU,OAAO;EAC9D;CACF;CAED,eAAe,YAAYC,MAAqC;AAC9D,UAAQ,SAAS;AACjB,aAAW,SAAS,KAAK,KAAK,GAAG;AACjC,QAAM,UAAU;AAChB,gBAAc,KAAK,GAAG;CACvB;CAED,SAAS,YAAYC,SAAsB;AACzC,WAAS,QAAQ,QAAQ;AACzB,MAAI,mBAAmB,iBAAiB,gBAAgB,SAAS,QAAQ,KAAK,CAAE,aAAY,MAAM;CACnG;AAED,QACE,4BAAC,OAAA,EAAA,UAAA;EACC,4BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UAAA;IACZ,2BAAC,SAAA;KACC,OAAO,EAAE;KACT,aAAY;KACZ,OAAO;KACP,UAAU,CAAA,MAAK,SAAS,EAAE,OAAO,MAAM;IAAA,EACvC;IACF,2BAAC,UAAA;KACC,MAAK;KACL,OAAO;MAAE,GAAG,EAAE;MAAe,IAAK,WAAW,EAAE,iBAAiB;KAAO;KACvE,WAAW;KACX,SAAS,MAAM;AAAE,cAAQ,SAAS;AAAE,oBAAc,KAAK;KAAE;KACzD,OAAO,WAAW,UAAU;KAAA,UAC7B;IAAA,EAAgB;IACjB,2BAAC,UAAA;KAAO,MAAK;KAAS,OAAO,EAAE;KAAQ,UAAU;KAAS,SAAS,WAAW,UAAU;KAAA,UAAE;IAAA,EAAW;GAAA;EAAA,EACjG;EAEL,UAAU,OAEP,4BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UAAA,CACZ,2BAAC,QAAA,EAAA,UAAM,MAAA,EAAa,EACpB,2BAAC,QAAA;IAAK,OAAO;KAAE,GAAG,EAAE;KAAM,YAAY;IAAQ;IAAE,SAAS,MAAM,SAAS,KAAK;IAAA,UAAE;GAAA,EAAS;EAAA,EACpF,GAEN;EAEH,WAAW,OAER,4BAAC,OAAA;GAAI,OAAO;IAAE,GAAG,EAAE;IAAQ,aAAa,EAAE;IAAI,OAAO,EAAE;GAAI;GAAA,UAAA,CACzD,2BAAC,QAAA,EAAA,UAAM,OAAA,EAAc,EACrB,2BAAC,QAAA;IAAK,OAAO;KAAE,GAAG,EAAE;KAAM,YAAY;IAAQ;IAAE,SAAS,MAAM,UAAU,KAAK;IAAA,UAAE;GAAA,EAAS;EAAA,EACrF,GAEN;EAEH,cAAc,OAEX,4BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UAAA;IACZ,2BAAC,QAAA,EAAA,UAAK,qBAAA,EAAyB;IAC/B,2BAAC,UAAA;KAAO,MAAK;KAAS,OAAO;MAAE,GAAG,EAAE;MAAc,YAAY;KAAQ;KAAE,SAAS,WAAW,aAAa,UAAU;KAAA,UAAE;IAAA,EAAW;IAChI,2BAAC,UAAA;KAAO,MAAK;KAAS,OAAO,EAAE;KAAQ,SAAS,MAAM,aAAa,KAAK;KAAA,UAAE;IAAA,EAAW;GAAA;EAAA,EACjF,GAEN;EAEJ,2BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UACZ,4BAAC,SAAA;IAAM,OAAO,EAAE;IAAA,UAAA,CACd,2BAAC,SAAA,EAAA,UACC,4BAAC,MAAA,EAAA,UAAA;KACC,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAO;KACxB,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAO;KACxB,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAO;KACxB,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAO;KACxB,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAO;KACxB,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAS;KAC1B,2BAAC,MAAA;MAAG,OAAO,EAAE;MAAA,UAAI;KAAA,EAAO;IAAA,EAAA,EACrB,CAAA,EACC,EACR,2BAAC,SAAA,EAAA,UACE,QAAQ,WAAW,MAAM,UAEtB,2BAAC,MAAA,EAAA,UACC,2BAAC,MAAA;KAAG,OAAO;MAAE,GAAG,EAAE;MAAI,OAAO,EAAE;MAAK,WAAW;MAAU,SAAS;KAAY;KAAE,SAAS;KAAA,UAAG;IAAA,EAEvF,CAAA,EACF,GAEL,QAAQ,IAAI,CAAA,SACZ,4BAAC,MAAA;KAEC,OAAO;MAAE,GAAG,EAAE;MAAK,GAAI,KAAK,OAAO,aAAa,EAAE,cAAc;KAAO;KACvE,SAAS,MAAM;AAAE,oBAAc,KAAK,GAAG;AAAE,cAAQ,SAAS;KAAE;KAAA,UAAA;MAE5D,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAI,OAAO,KAAK,eAAe;OAAA,UAAK,KAAK;MAAA,EAAU;MAChE,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UACX,2BAAC,QAAA;QACC,OAAO;SACL,SAAS;SAAgB,SAAS;SAAS,cAAc;SAAG,UAAU;SACtE,OAAO,WAAW,KAAK,SAAS,EAAE;SAAK,SAAS,YAAY,WAAW,KAAK,SAAS,EAAE,OAAO;QAC/F;QAAA,UACD,yBAAyB,KAAK,SAAS,KAAK;OAAA,EAAY;MAAA,EACvD;MACL,4BAAC,MAAA;OAAG,OAAO,EAAE;OAAI,QAAQ,EAAE,KAAK,KAAK,GAAG,KAAK,KAAK;OAAA,UAAA;QAAI,KAAK;QAAK;QAAE,KAAK;OAAA;MAAA,EAAU;MACjF,2BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAK,KAAK;MAAA,EAAc;MACrC,4BAAC,MAAA;OAAG,OAAO,EAAE;OAAA,UAAA,CACX,2BAAC,QAAA,EAAK,OAAO;QAAE,GAAG,EAAE;QAAK,YAAY,oBAAoB,KAAK,WAAW,EAAE;OAAK,EAAA,EAAI,EACnF,mBAAmB,KAAK,WAAW,KAAK,MAAA;MAAA,EACtC;MACL,2BAAC,MAAA;OAAG,OAAO;QAAE,GAAG,EAAE;QAAI,OAAO,EAAE;OAAK;OAAA,UACjC,KAAK,kBAAkB,OAAO,MAAM,OAAO,KAAK,cAAc;MAAA,EAC5D;MACL,4BAAC,MAAA;OAAG,OAAO;QAAE,GAAG,EAAE;QAAI,YAAY;OAAU;OAAA,UAAA;QAC1C,2BAAC,QAAA;SACC,OAAO;UAAE,GAAG,EAAE;UAAM,aAAa;UAAG,GAAI,cAAc,OAAO,EAAE,iBAAiB;SAAO;SACvF,SAAS,CAAA,UAAS;AAChB,gBAAM,iBAAiB;AACvB,cAAI,cAAc,KAAM,MAAK,WAAW,KAAK,GAAG;SACjD;SAAA,UACD,cAAc,KAAK,KAAK,SAAS;QAAA,EAAY;QAC/C,2BAAC,QAAA;SACC,OAAO;UAAE,GAAG,EAAE;UAAM,aAAa;UAAG,IAAK,WAAW,EAAE,iBAAiB;SAAO;SAC9E,SAAS,CAAA,UAAS;AAChB,gBAAM,iBAAiB;AACvB,eAAK,SAAU;AACf,wBAAc,KAAK,GAAG;AACtB,kBAAQ,OAAO;SAChB;SAAA,UACF;QAAA,EAAS;QACV,2BAAC,QAAA;SACC,OAAO;UAAE,GAAG,EAAE;UAAM,OAAO,EAAE;UAAQ,IAAK,WAAW,EAAE,iBAAiB;SAAO;SAC/E,SAAS,CAAA,UAAS;AAChB,gBAAM,iBAAiB;AACvB,cAAI,SAAU,cAAa,KAAK,GAAG;SACpC;SAAA,UACF;QAAA,EAAS;OAAA;MAAA,EACP;KAAA;IAAA,GA9CA,KAAK,GA+CP,CACL,CAAA,EACE;GAAA,EACF;EAAA,EACJ;EAEL,SAAS,WAEN,2BAAC,gBAAA;GACC,SAAS,CAAA,cAAa,YAAY,KAAK;GACvC,UAAU,MAAM,QAAQ,SAAS;GACjC,SAAS;EAAA,EACT,GAEF,SAAS,UAAU,aAAa,OAE9B,2BAAC,gBAAA;GACC,SAAS;GACT,SAAS,CAAA,cAAa,YAAY,KAAK;GACvC,UAAU,MAAM,QAAQ,SAAS;GACjC,SAAS;EAAA,EACT,GAEF,aAAa,OAEX,4BAAA,4BAAA,EAAA,UAAA,CACE,4BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UAAA;IACZ,4BAAC,OAAA;KAAI,OAAO;MAAE,SAAS;MAAQ,gBAAgB;MAAiB,cAAc;KAAG;KAAA,UAAA,CAC/E,2BAAC,UAAA;MAAO,OAAO,EAAE,UAAU,GAAI;MAAA,UAAG,SAAS;KAAA,EAAc,EACzD,4BAAC,QAAA;MAAK,OAAO,EAAE;MAAA,UAAA;OACZ,yBAAyB,SAAS,SAAS,SAAS;OAAK;OAAS,OAAO,SAAS,UAAU;MAAA;KAAA,EACxF;IAAA,EACH;IACN,4BAAC,OAAA;KAAI,OAAO,EAAE;KAAA,UAAA;MACZ,4BAAC,OAAA,EAAA,UAAA;OAAI,2BAAC,QAAA;QAAK,OAAO,EAAE;QAAA,UAAO;OAAA,EAAU;OAAC,SAAS;OAAK;OAAE,SAAS;MAAA,EAAA,EAAW;MAC1E,4BAAC,OAAA,EAAA,UAAA,CAAI,2BAAC,QAAA;OAAK,OAAO,EAAE;OAAA,UAAO;MAAA,EAAU,EAAC,SAAS,QAAA,EAAA,EAAe;MAC9D,4BAAC,OAAA,EAAA,UAAA,CAAI,2BAAC,QAAA;OAAK,OAAO,EAAE;OAAA,UAAO;MAAA,EAAW,EAAC,SAAS,QAAA,EAAA,EAAe;MAC/D,4BAAC,OAAA,EAAA,UAAA,CACC,2BAAC,QAAA;OAAK,OAAO,EAAE;OAAA,UAAO;MAAA,EAAU,EAC/B,SAAS,cAAc,eAAe,KAAA,EAAA,EACnC;MACN,4BAAC,OAAA,EAAA,UAAA,CAAI,2BAAC,QAAA;OAAK,OAAO,EAAE;OAAA,UAAO;MAAA,EAAW,EAAC,SAAS,WAAW,GAAA,EAAA,EAAU;MACrE,4BAAC,OAAA,EAAA,UAAA,CAAI,2BAAC,QAAA;OAAK,OAAO,EAAE;OAAA,UAAO;MAAA,EAAa,EAAC,SAAS,WAAW,GAAA,EAAA,EAAU;KAAA;IAAA,EACnE;IACL,SAAS,gBAAgB,OACtB,2BAAC,OAAA;KAAI,OAAO;MAAE,GAAG,EAAE;MAAO,WAAW;KAAG;KAAA,UAAG,SAAS;IAAA,EAAkB,GACtE;IACH,SAAS,cAAc,OACpB,4BAAC,OAAA;KAAI,OAAO;MAAE,GAAG,EAAE;MAAO,WAAW;MAAG,OAAO,EAAE;KAAQ;KAAA,UAAA,CAAE,SAAM,SAAS,SAAA;IAAA,EAAgB,GAC1F;GAAA;EAAA,EACA,EACN,2BAAC,mBAAA;GAAkB,QAAQ;GAAkB;GAAQ,SAAS;EAAA,EAAe,EAAA,EAC5E,GAGH,2BAAC,OAAA;GAAI,OAAO;IAAE,GAAG,EAAE;IAAO,WAAW;GAAI;GAAA,UACtC,UAAU,SAAS;EAAA,EAChB;CAAA,EAAA,EAEV;AAET;;;;AC3RD,MAAMC,OAAsC,CAC1C;CAAE,KAAK;CAAY,OAAO;AAAO,GACjC;CAAE,KAAK;CAAW,OAAO;AAC1B,CAAA;AAEM,SAAS,yBAAuC;CACrD,MAAM,CAAC,KAAK,OAAO,GAAG,oBAAc,WAAW;AAE/C,QACE,4BAAC,OAAA;EAAI,OAAO,EAAE;EAAA,UAAA,CACZ,2BAAC,OAAA;GAAI,OAAO,EAAE;GAAA,UACX,KAAK,IAAI,CAAA,UACR,2BAAC,UAAA;IAEC,MAAK;IACL,OAAO;KAAE,GAAG,EAAE;KAAK,GAAI,QAAQ,MAAM,MAAM,EAAE,YAAY;IAAO;IAChE,SAAS,MAAM,OAAO,MAAM,IAAI;IAAA,UAChC,MAAM;GAAA,GAJD,MAAM,IAIU,CACvB;EAAA,EACE,EACL,QAAQ,aAAa,2BAAC,eAAA,CAAA,EAAgB,GAAG,2BAAC,kBAAA,CAAA,EAAmB;CAAA,EAC1D;AAET;;;;;ACrBD,MAAM,WAAW;AACjB,MAAM,gBAAgB;AA8CtB,SAAS,WAAWC,OAA6C;AAC/D,YAAW,UAAU,YAAY,UAAU,KAAM,QAAO;CACxD,MAAM,OAAO;AACb,KAAI,KAAK,SAAS,eAAgB,QAAO;AACzC,YAAW,KAAK,WAAW,mBAAmB,KAAK,aAAa,SAAU,QAAO;AACjF,QAAO;AACR;AAID,MAAM,SAAS;CACb,MAAM;EAAE,QAAQ;EAAS,UAAU;EAAI,OAAO;CAAiC;CAC/E,MAAM;EAAE,SAAS;EAAQ,YAAY;EAAU,KAAK;EAAG,cAAc;CAAG;CACxE,OAAO;EAAE,YAAY;EAAK,OAAO;CAA0B;CAC3D,QAAQ;EAAE,WAAW;EAAK,UAAU;EAAQ,QAAQ;EAAwC,cAAc;CAAG;CAC7G,OAAO;EAAE,gBAAgB;EAAY,OAAO;EAAQ,UAAU;CAAI;CAClE,IAAI;EACF,UAAU;EAAU,KAAK;EAAG,YAAY;EACxC,WAAW;EAAQ,SAAS;EAAW,cAAc;EACrD,YAAY;CACb;CACD,UAAU;EAAE,YAAY;EAAQ,QAAQ;EAAQ,QAAQ;EAAW,MAAM;EAAW,SAAS;EAAG,OAAO;CAAW;CAClH,IAAI;EAAE,SAAS;EAAW,cAAc;EAAqC,UAAU;EAAK,UAAU;EAAU,cAAc;EAAY,YAAY;CAAU;CAChK,KAAK;EAAE,SAAS;EAAQ,YAAY;EAAU,KAAK;EAAG,WAAW;EAAG,UAAU;CAAQ;CACtF,QAAQ;EAAE,QAAQ;EAAqC,YAAY;EAAe,cAAc;EAAG,SAAS;EAAW,QAAQ;EAAW,UAAU;CAAI;CACxJ,OAAO,EAAE,SAAS,GAAK;CACvB,OAAO,EAAE,OAAO,6BAA8B;CAC9C,MAAM;EAAE,YAAY;EAAW,OAAO;EAAW,SAAS;EAAY,cAAc;EAAG,UAAU;EAAI,cAAc;CAAG;AACvH;AAED,SAAS,WAAWA,OAAwB;AAC1C,KAAI,UAAU,QAAQ,UAAU,UAAW,QAAO;AAClD,YAAW,UAAU,SACnB,KAAI;AACF,SAAO,KAAK,UAAU,MAAM;CAC7B,QAAO;AACN,SAAO;CACR;AAEH,QAAO,OAAO,MAAM;AACrB;;AAKD,MAAM,kBAAkB;;AAExB,MAAM,mBAAmB;AAEzB,SAAS,UAAUA,OAAwB;CACzC,IAAI,OAAO,WAAW,MAAM;AAC5B,KAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,YAAW,UAAU,YAAY,iBAAiB,KAAK,KAAK,CAAE,SAAQ,GAAG,KAAK;AAC9E,KAAI,gBAAgB,KAAK,KAAK,CAAE,SAAQ,GAAG,KAAK,QAAQ,MAAM,OAAK,CAAC;AACpE,QAAO;AACR;AAED,SAAS,SAASC,OAA0B;AAC1C,QAAO,MAAM,IAAI,CAAA,SAAQ,UAAU,KAAK,CAAC,CAAC,KAAK,IAAI;AACpD;;AAGD,SAAS,MAAMC,SAAmBC,MAAyC;CACzE,MAAM,QAAQ,CAAC,SAAS,QAAQ,AAAC;AACjC,MAAK,MAAM,OAAO,KAAM,OAAM,KAAK,SAAS,QAAQ,IAAI,CAAA,WAAU,IAAI,QAAQ,CAAC,CAAC;AAChF,SAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;AAC9B;;AAGD,SAAS,UAAUD,SAAmBC,MAAyC;AAC7E,KAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,SAAQ,EAAE,KAAK,IAAI,CAAA,QAAO,SAAS,QAAQ,IAAI,CAAA,WAAU,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;AACtF;;AAGD,SAAS,iBAAiBC,QAAcC,MAAY,IAAI,QAAgB;CACtE,MAAM,MAAM,CAACC,UAA0B,OAAO,MAAM,CAAC,SAAS,GAAG,IAAI;CACrE,MAAM,SAAS,EAAE,IAAI,aAAa,CAAC,EAAE,IAAI,IAAI,UAAU,GAAG,EAAE,CAAC,EAAE,IAAI,IAAI,SAAS,CAAC,CAAC,KAC7E,GAAG,IAAI,IAAI,UAAU,CAAC,CAAC,EAAE,IAAI,IAAI,YAAY,CAAC,CAAC,EAAE,IAAI,IAAI,YAAY,CAAC,CAAC;CAC5E,MAAM,OAAO,OAAK,QAAQ,uBAAuB,IAAI,CAAC,MAAM;AAC5D,SAAQ,EAAE,KAAK,SAAS,IAAI,OAAO,UAAU,GAAG,MAAM;AACvD;;AAGD,SAAS,YAAYC,UAAkBC,MAAoB;CACzD,MAAM,MAAM,IAAI,gBAAgB,IAAI,KAAK,EAAE,QAAQ,KAAK,CAAE,GAAE,EAAE,MAAM,yBAA0B,GAAE;CAChG,MAAM,OAAO,SAAS,cAAc,IAAI;AACxC,MAAK,OAAO;AACZ,MAAK,WAAW;AAChB,MAAK,MAAM,UAAU;AACrB,UAAS,KAAK,YAAY,KAAK;AAC/B,MAAK,OAAO;AACZ,MAAK,QAAQ;AAEb,YAAW,MAAM,IAAI,gBAAgB,IAAI,EAAE,EAAE;AAC9C;AAiBD,SAAS,gBAAgBC,OAAqD;CAC5E,MAAM,OAAO,mBAAQ,MAAM,WAAW,MAAM,OAAO,KAAK,EAAE,CAAC,MAAM,KAAM,EAAC;CACxE,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAS,EAAE;CACnC,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAgE,UAAU;CAClG,MAAM,CAAC,OAAO,SAAS,GAAG,oBAAoB,EAAE,QAAQ,UAAW,EAAC;CACpE,MAAM,YAAY,kBAAO,EAAE;CAE3B,MAAM,cAAc,kBAAoC,UAAU;CAClE,MAAM,CAAC,aAAa,eAAe,GAAG,oBAAsB,EAAE,SAAS,MAAO,EAAC;AAE/E,sBAAU,MAAM,MAAM,YAAY,SAAS,OAAO,EAAE,CAAE,EAAC;AAEvD,sBAAU,MAAM;AACd,MAAI,SAAS,UAAW;EACxB,MAAM,aAAa,IAAI;EACvB,MAAM,KAAK,UAAU,UAAU;AAC/B,YAAU,UAAU;AACpB,WAAS,CAAA,aAAY,SAAS,WAAW,UAAU;GAAE,GAAG;GAAU,QAAQ;EAAW,IAAG,SAAS;EAEjG,MAAM,MAAM,IAAI,IAAI,KAAK,UAAU,OAAO,SAAS;AACnD,MAAI,aAAa,IAAI,QAAQ,OAAO,KAAK,CAAC;AAC1C,MAAI,aAAa,IAAI,YAAY,OAAO,KAAK,SAAS,CAAC;AACvD,MAAI,SAAS,WAAW;AACtB,OAAI,aAAa,IAAI,QAAQ,KAAK,OAAO;AACzC,OAAI,aAAa,IAAI,SAAS,KAAK,MAAM;EAC1C;AAED,OAAK,CAAC,YAAY;AAChB,OAAI;IACF,MAAM,WAAW,MAAM,MAAM,IAAI,UAAU,EAAE;KAAE,QAAQ,WAAW;KAAQ,aAAa;IAAe,EAAC;AACvG,SAAK,SAAS,IAAI;KAChB,MAAM,SAAO,MAAM,SAAS,MAAM,CAAC,MAAM,MAAM,KAAK;AACpD,SAAI,OAAO,UAAU,QAEnB,UAAS,CAAA,cAAa;MACpB,QAAQ;MACR,SAAS,SAAS;MAClB,SAAS,QAAM,OAAO,SAAS,oBAAoB,SAAS,WAAW,MACnE,oCACC,YAAY,SAAS,OAAO;KAClC,GAAE;AAEL;IACD;IACD,MAAM,YAAU,MAAM,SAAS,MAAM;AACrC,QAAI,OAAO,UAAU,QAAS,UAAS;KAAE,QAAQ;KAAS;IAAS,EAAC;GACrE,SAAQ,OAAO;AACd,QAAI,WAAW,OAAO,QAAS;AAC/B,QAAI,OAAO,UAAU,QAAS,UAAS;KAAE,QAAQ;KAAS,UAAU,OAAO,OAAO,MAAM,CAAC;IAAG,EAAC;GAC9F;EACF,IAAG;AAEJ,SAAO,MAAM,WAAW,OAAO;CAChC,GAAE;EAAC;EAAM;EAAM;CAAK,EAAC;CAEtB,MAAM,aAAa,uBAAY,CAACC,WAAmB;AACjD,MAAI,SAAS,cAAc,KAAK,SAAS,SAAS,OAAO,CAAE;AAE3D,MAAI,YAAY,YAAY,WAAW;AACrC,eAAY,QAAQ,OAAO;AAC3B,eAAY,UAAU;AACtB,kBAAe,EAAE,SAAS,MAAO,EAAC;EACnC;AACD,UAAQ,EAAE;AACV,UAAQ,CAAA,aAAY,UAAU,WAAW,SACrC;GAAE;GAAQ,OAAO,SAAS,UAAU,QAAQ,SAAS;EAAO,IAC5D;GAAE;GAAQ,OAAO;EAAO,EAAC;CAC9B,GAAE,CAAC,IAAK,EAAC;;;;;;;;;;CAOV,MAAM,YAAY,uBAAY,MAAM;AAClC,MAAI,SAAS,UAAW;AACxB,cAAY,SAAS,OAAO;EAC5B,MAAM,aAAa,IAAI;AACvB,cAAY,UAAU;AACtB,iBAAe;GAAE,SAAS;GAAM,UAAU;IAAE,MAAM;IAAG,OAAO,KAAK;GAAW;EAAE,EAAC;AAE/E,OAAK,CAAC,YAAY;AAChB,OAAI;IACF,MAAM,OAAO,IAAI,IAAI,KAAK,UAAU,OAAO,SAAS;AACpD,SAAK,aAAa,IAAI,YAAY,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,cAAc,IAAI,KAAK,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC;AAC3H,QAAI,SAAS,WAAW;AACtB,UAAK,aAAa,IAAI,QAAQ,KAAK,OAAO;AAC1C,UAAK,aAAa,IAAI,SAAS,KAAK,MAAM;IAC3C;IAED,MAAMC,SAAmB,CAAE;IAC3B,IAAI,gBAAgB,KAAK,QAAQ,IAAI,CAAA,WAAU,OAAO,KAAK;IAC3D,IAAI,YAAY,KAAK;IACrB,IAAI,eAAa;IACjB,IAAI,OAAO;AAEX,SAAK,IAAI,QAAQ,GAAG,SAAS,cAAY,SAAS,GAAG;AACnD,SAAI,WAAW,OAAO,QAAS;KAC/B,MAAM,MAAM,IAAI,IAAI,KAAK,UAAU;AACnC,SAAI,aAAa,IAAI,QAAQ,OAAO,MAAM,CAAC;KAC3C,MAAM,WAAW,MAAM,MAAM,IAAI,UAAU,EAAE;MAAE,QAAQ,WAAW;MAAQ,aAAa;KAAe,EAAC;AACvG,UAAK,SAAS,IAAI;MAChB,MAAM,SAAO,MAAM,SAAS,MAAM,CAAC,MAAM,MAAM,KAAK;AACpD,UAAI,WAAW,OAAO,QAAS;AAC/B,YAAM,IAAI,MAAM,QAAM,OAAO,SAAS,oBAAoB,SAAS,WAAW,MAC1E,oCACC,YAAY,SAAS,OAAO;KAClC;KACD,MAAM,UAAU,MAAM,SAAS,MAAM;AACrC,SAAI,WAAW,OAAO,QAAS;AAC/B,SAAI,UAAU,GAAG;AAEf,sBAAgB,QAAQ,QAAQ,SAAS,IAAI,QAAQ,UAAU;AAC/D,kBAAY,QAAQ;AACpB,qBAAa,KAAK,IAAI,GAAG,QAAQ,WAAW;AAC5C,aAAO,KAAK,MAAM,eAAe,QAAQ,KAAK,CAAC;KAChD,MACC,QAAO,KAAK,UAAU,eAAe,QAAQ,KAAK,CAAC;AAErD,aAAQ,QAAQ,KAAK;AACrB,oBAAe;MAAE,SAAS;MAAM,UAAU;OAAE;OAAM,OAAO;MAAW;KAAE,EAAC;IACxE;AAED,gBAAY,iBAAiB,KAAK,KAAK,EAAE,OAAO,KAAK,GAAG,CAAC;AACzD,QAAI,YAAY,YAAY,WAAY,aAAY,UAAU;AAC9D,mBAAe,EAAE,SAAS,MAAO,EAAC;GACnC,SAAQ,OAAO;AACd,QAAI,WAAW,OAAO,QAAS;AAC/B,QAAI,YAAY,YAAY,WAAY,aAAY,UAAU;AAC9D,mBAAe;KAAE,SAAS;KAAO,OAAO,iBAAiB,QAAQ,MAAM,WAAW,OAAO,OAAO,MAAM,CAAC;IAAG,EAAC;GAC5G;EACF,IAAG;CACL,GAAE,CAAC,MAAM,IAAK,EAAC;AAGhB,KAAI,SAAS,WAAW;EACtB,MAAM,OAAO,CAAC,MAAM,OAAO,WAAW,CAAE,GACrC,OAAO,CAAA,UAAS,MAAM,SAAS,OAAO,CACtC,IAAI,CAAA,UAAS,MAAM,QAAQ,GAAG,CAC9B,KAAK,KAAK;AACb,SAAO,cAAM,cAAc,OAAO,EAAE,OAAO,OAAO,KAAM,GAAE,cAAM,cAAc,OAAO,EAAE,OAAO;GAAE,QAAQ;GAAG,YAAY;EAAY,EAAE,GAAE,KAAK,CAAC;CAC9I;CAED,MAAM,UAAU,MAAM;CACtB,MAAM,UAAU,SAAS,WAAW,KAAK,QAAQ,IAAI,CAAA,WAAU,OAAO,KAAK;CAC3E,MAAM,OAAO,SAAS,QAAQ,CAAE;CAChC,MAAM,aAAa,SAAS,cAAc,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,YAAY,KAAK,SAAS,CAAC;CAChG,MAAM,OAAO,MAAM,WAAW,aAAa,YAAY;CAEvD,MAAM,eAAe,YAAY,YAAY,KAAK,YAAY,QAAQ,aAAa,QAAQ;CAE3F,MAAM,OAAO,cAAM,cAAc,MAAM,MAAM,GAAG,QAAQ,IAAI,CAAA,WAAU,cAAM,cAC1E,MACA;EAAE,KAAK;EAAQ,OAAO,OAAO;CAAI,GACjC,KAAK,SAAS,SAAS,OAAO,GAC1B,cAAM,cAAc,UAAU;EAC9B,MAAM;EACN,OAAO,OAAO;EACd,SAAS,MAAM,WAAW,OAAO;EACjC,OAAO;CACR,IAAG,EAAE,OAAO,EAAE,MAAM,WAAW,SAAU,KAAK,UAAU,QAAQ,OAAO,OAAQ,GAAG,EAAE,GACnF,OACL,CAAC,CAAC;CAEH,MAAM,OAAO,KAAK,IAAI,CAAC,KAAK,UAAU,cAAM,cAAc,MAAM,EAAE,KAAK,OAAO,IAAI,WAAW,MAAM,CAAE,GACnG,GAAG,QAAQ,IAAI,CAAA,WAAU,cAAM,cAAc,MAAM;EAAE,KAAK;EAAQ,OAAO,OAAO;EAAI,OAAO,WAAW,IAAI,QAAQ;CAAE,GAAE,WAAW,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;AAEnJ,QAAO,cAAM,cAAc,OAAO,EAAE,OAAO,OAAO,KAAM,GACtD,cAAM,cAAc,OAAO,EAAE,OAAO,OAAO,KAAM,GAAE,sBAAsB,EACzE,cAAM,cAAc,OAAO,EAAE,OAAO,OAAO,KAAM,GAC/C,cAAM,cAAc,QAAQ,EAAE,OAAO,OAAO,MAAO,IAAG,MAAM,KAAK,KAAK,EAAE,EACxE,cAAM,cAAc,QAAQ,OAAO,IAAI,KAAK,UAAU,OAAO,KAAK,QAAQ,OAAO,IAAI,EACrF,eAAe,KAAK,YAChB,cAAM,cAAc,QAAQ,EAAE,OAAO,OAAO,MAAO,IAAG,SAAS,aAAa,KAAK,GACjF,MACJ,MAAM,WAAW,UAAU,cAAM,cAAc,QAAQ,EAAE,OAAO,OAAO,MAAO,GAAE,MAAM,WAAW,GAAG,GAAG,MACvG,OAAO,cAAM,cAAc,QAAQ,EAAE,OAAO,OAAO,MAAO,GAAE,OAAO,GAAG,KACvE,EACD,cAAM,cAAc,OAAO,EAAE,OAAO,OAAO,OAAQ,GACjD,cAAM,cAAc,SAAS,EAAE,OAAO,OAAO,MAAO,GAClD,cAAM,cAAc,SAAS,MAAM,KAAK,EACxC,cAAM,cAAc,SAAS,MAAM,GAAG,KAAK,CAAC,CAAC,EACjD,cAAM,cAAc,OAAO,EAAE,OAAO,OAAO,IAAK,GAC9C,cAAM,cAAc,UAAU;EAAE,MAAM;EAAU,OAAO,OAAO;EAAQ,UAAU,QAAQ;EAAG,SAAS,MAAM,QAAQ,EAAE;CAAE,GAAE,OAAO,EAC/H,cAAM,cAAc,UAAU;EAAE,MAAM;EAAU,OAAO,OAAO;EAAQ,UAAU,QAAQ;EAAG,SAAS,MAAM,QAAQ,CAAA,YAAW,KAAK,IAAI,GAAG,UAAU,EAAE,CAAC;CAAE,GAAE,MAAM,EAChK,cAAM,cAAc,QAAQ,OAAO,IAAI,SAAS,QAAQ,KAAK,KAAK,WAAW,IAAI,EACjF,cAAM,cAAc,UAAU;EAC5B,MAAM;EACN,OAAO,OAAO;EACd,WAAW,KAAK,UAAU,QAAQ;EAClC,SAAS,MAAM,QAAQ,CAAA,YAAW,KAAK,IAAI,YAAY,UAAU,EAAE,CAAC;CACrE,GAAE,MAAM,GACR,KAAK,SAAS,cAAM,cAAc,QAAQ,EAAE,OAAO,OAAO,MAAO,GAAE,qBAAqB,GAAG,MAC5F,cAAM,cAAc,UAAU;EAC5B,MAAM;EACN,OAAO,OAAO;EACd,UAAU,YAAY,WAAW,MAAM,WAAW;EAClD,QAAQ,gBAAgB,aAAa;EACrC,SAAS;CACV,GAAE,YAAY,WACV,OAAO,YAAY,UAAU,QAAQ,EAAE,KAAK,YAAY,UAAU,SAAS,aAAa,OACzF,SAAS,EACb,YAAY,UAAU,YAClB,OACA,cAAM,cAAc,QAAQ,EAAE,OAAO,OAAO,MAAO,GAAE,YAAY,MAAM,CAC5E,CAAC;AACL;MAIY,OAAO;MACP,SAAS,CAAC,OAAQ;AAExB,SAAS,MAAMC,KAA0B;CAC9C,MAAM,QAAS,IAA4C;AAC3D,KAAI,UAAU,UAAW;AACzB,OAAM,OAAO,eAAe,MAAM,MAAM,SAAS;EAAE,MAAM;EAAe,KAAK;CAAU,GAAE,gBAAgB,CAAC;AAE1G,OAAM,OAAO,oBAAoB,MAC/B,MAAM,SACJ;EAAE,MAAM;EAAoB,IAAI;EAAkB,OAAO;EAAqB,OAAO;CAAqB,GAC1G,uBACD,CAAC;AACL"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 远端列 → 本地 `ColumnInfo` 的映射,以及行键改写。
|
|
3
|
+
*
|
|
4
|
+
* 与文件导入的区别:远端已有权威类型声明,所以**不做采样推断**(`parse.ts` 的
|
|
5
|
+
* `inferColumnType` 只在解析 Excel/CSV 时用),直接采用连接器映射出的类型。
|
|
6
|
+
*/
|
|
7
|
+
import { type ColumnInfo } from '../parse';
|
|
8
|
+
import type { RemoteColumn } from './types';
|
|
9
|
+
export declare function buildColumns(remote: readonly RemoteColumn[]): ColumnInfo[];
|
|
10
|
+
/** `insertRows` 按 `sanitizedName` 取值,远端行的原始列键必须改写一次。 */
|
|
11
|
+
export declare function mapRowKeys(rows: readonly Record<string, unknown>[], columns: readonly ColumnInfo[]): Record<string, unknown>[];
|
|
12
|
+
/** 从首批行里取几个非空值填进 `sample`(原地修改)。 */
|
|
13
|
+
export declare function fillSamples(columns: readonly ColumnInfo[], rows: readonly Record<string, unknown>[]): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 连接器的工厂与缓存。
|
|
3
|
+
*
|
|
4
|
+
* key 是 `sourceId`:同一数据源在一次插件生命周期内复用同一个连接池。
|
|
5
|
+
* 数据源被改 / 删时必须先 `closeConnector`,否则缓存里会残留旧凭据的连接。
|
|
6
|
+
*/
|
|
7
|
+
import type { DatabaseConnector } from './connector/base';
|
|
8
|
+
import type { ConnectionConfig, ConnectionTestResult, DataSourceConfig, DataSourceRecord, DataSourceType } from './types';
|
|
9
|
+
export declare const DEFAULT_PORTS: Readonly<Record<DataSourceType, number>>;
|
|
10
|
+
/** 未保存的连接参数(设置页「先测试再保存」用)。 */
|
|
11
|
+
export interface ConnectionDraft {
|
|
12
|
+
type: DataSourceType;
|
|
13
|
+
host: string;
|
|
14
|
+
port: number | null;
|
|
15
|
+
database: string;
|
|
16
|
+
username: string;
|
|
17
|
+
password: string;
|
|
18
|
+
sslMode?: string | null;
|
|
19
|
+
poolMax?: number | null;
|
|
20
|
+
}
|
|
21
|
+
export declare function createConnector(type: DataSourceType, config: ConnectionConfig): DatabaseConnector;
|
|
22
|
+
/** 已登记数据源 → 明文连接参数(解密只发生在这一刻,结果不出本模块)。 */
|
|
23
|
+
export declare function connectionOf(cfg: DataSourceConfig, record: DataSourceRecord): ConnectionConfig;
|
|
24
|
+
/** 未保存草稿 → 明文连接参数。 */
|
|
25
|
+
export declare function draftToConfig(cfg: DataSourceConfig, draft: ConnectionDraft): ConnectionConfig;
|
|
26
|
+
/** 取(或建)一个已连通的连接器;连接失败会抛出,并把失败状态留给调用方记录。 */
|
|
27
|
+
export declare function connectSource(cfg: DataSourceConfig, record: DataSourceRecord): Promise<DatabaseConnector>;
|
|
28
|
+
/** 用一次性连接器测连接:测完立刻关闭,不进缓存。 */
|
|
29
|
+
export declare function testDraft(cfg: DataSourceConfig, draft: ConnectionDraft): Promise<ConnectionTestResult>;
|
|
30
|
+
/** 测一条已登记的数据源:命中缓存就复用,否则建一次性连接。 */
|
|
31
|
+
export declare function testSource(cfg: DataSourceConfig, record: DataSourceRecord): Promise<ConnectionTestResult>;
|
|
32
|
+
export declare function closeConnector(sourceId: string): Promise<void>;
|
|
33
|
+
export declare function closeAllConnectors(): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据库连接器抽象基类。
|
|
3
|
+
*
|
|
4
|
+
* 只定义方言无关的能力:连通性测试、schema / 表 / 列元数据、分块取数据。
|
|
5
|
+
* 标识符引号化与类型映射由各子类实现,基类不写任何方言分支。
|
|
6
|
+
*/
|
|
7
|
+
import type { ConnectionConfig, ConnectionTestResult, FetchRange, RemoteColumn, RemoteTable, SchemaSummary } from '../types';
|
|
8
|
+
export declare abstract class DatabaseConnector {
|
|
9
|
+
protected readonly config: ConnectionConfig;
|
|
10
|
+
constructor(config: ConnectionConfig);
|
|
11
|
+
abstract connect(): Promise<void>;
|
|
12
|
+
abstract disconnect(): Promise<void>;
|
|
13
|
+
abstract testConnection(): Promise<ConnectionTestResult>;
|
|
14
|
+
abstract getSchemas(): Promise<SchemaSummary[]>;
|
|
15
|
+
abstract getTables(schemaName?: string): Promise<RemoteTable[]>;
|
|
16
|
+
abstract getTableColumns(tableName: string, schemaName?: string): Promise<RemoteColumn[]>;
|
|
17
|
+
abstract getTableData(tableName: string, schemaName?: string, range?: FetchRange): Promise<Record<string, unknown>[]>;
|
|
18
|
+
protected abstract quoteIdent(name: string): string;
|
|
19
|
+
protected tableRef(tableName: string, schemaName?: string | null): string;
|
|
20
|
+
protected buildSelectQuery(tableName: string, schemaName?: string | null, range?: FetchRange): string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MySQL 连接器。
|
|
3
|
+
*
|
|
4
|
+
* 元数据一次查完(`tables` LEFT JOIN `columns`),避免逐表 N+1 查询。
|
|
5
|
+
* 类型映射依据 `information_schema.COLUMN_TYPE`:远端已有权威声明,不再采样推断。
|
|
6
|
+
*/
|
|
7
|
+
import type { ConnectionTestResult, RemoteColumn, RemoteTable, SchemaSummary } from '../types';
|
|
8
|
+
import { DatabaseConnector } from './base';
|
|
9
|
+
export declare class MySQLConnector extends DatabaseConnector {
|
|
10
|
+
private pool;
|
|
11
|
+
connect(): Promise<void>;
|
|
12
|
+
disconnect(): Promise<void>;
|
|
13
|
+
testConnection(): Promise<ConnectionTestResult>;
|
|
14
|
+
getSchemas(): Promise<SchemaSummary[]>;
|
|
15
|
+
getTables(schemaName?: string): Promise<RemoteTable[]>;
|
|
16
|
+
getTableColumns(tableName: string, schemaName?: string): Promise<RemoteColumn[]>;
|
|
17
|
+
getTableData(tableName: string, schemaName?: string, range?: {
|
|
18
|
+
limit?: number;
|
|
19
|
+
offset?: number;
|
|
20
|
+
}): Promise<Record<string, unknown>[]>;
|
|
21
|
+
protected quoteIdent(name: string): string;
|
|
22
|
+
private query;
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL 连接器。
|
|
3
|
+
*
|
|
4
|
+
* 元数据走 `pg_catalog`:一次查询拿到 schema 内所有表的列、类型、注释与主键,
|
|
5
|
+
* 行数取 `reltuples`(规划器估计值,未分析过的表为 -1)。
|
|
6
|
+
*/
|
|
7
|
+
import type { ConnectionTestResult, RemoteColumn, RemoteTable, SchemaSummary } from '../types';
|
|
8
|
+
import { DatabaseConnector } from './base';
|
|
9
|
+
export declare class PostgreSQLConnector extends DatabaseConnector {
|
|
10
|
+
private pool;
|
|
11
|
+
connect(): Promise<void>;
|
|
12
|
+
disconnect(): Promise<void>;
|
|
13
|
+
testConnection(): Promise<ConnectionTestResult>;
|
|
14
|
+
getSchemas(): Promise<SchemaSummary[]>;
|
|
15
|
+
getTables(schemaName?: string): Promise<RemoteTable[]>;
|
|
16
|
+
getTableColumns(tableName: string, schemaName?: string): Promise<RemoteColumn[]>;
|
|
17
|
+
getTableData(tableName: string, schemaName?: string, range?: {
|
|
18
|
+
limit?: number;
|
|
19
|
+
offset?: number;
|
|
20
|
+
}): Promise<Record<string, unknown>[]>;
|
|
21
|
+
protected quoteIdent(name: string): string;
|
|
22
|
+
private query;
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据源密码的加解密:AES-256-GCM,密钥由 scryptSync 派生。
|
|
3
|
+
*
|
|
4
|
+
* 密文格式 `iv:authTag:encrypted`(十六进制)。落库只存密文,解密只在建立连接的
|
|
5
|
+
* 那一刻发生在内存里,任何日志 / 工具返回 / HTTP 响应都不出现明文。
|
|
6
|
+
*/
|
|
7
|
+
/** 优先级:配置项 → 环境变量 `LH_DATA_ENCRYPT_KEY` → 内置默认值(不安全,启动时会告警)。 */
|
|
8
|
+
export declare function resolveEncryptKey(configured: string): string;
|
|
9
|
+
/** 配置与环境变量都没给密钥时为 true(调用方据此告警一次)。 */
|
|
10
|
+
export declare function usingDefaultEncryptKey(configured: string): boolean;
|
|
11
|
+
export declare function encryptPassword(password: string, key: string): string;
|
|
12
|
+
export declare function decryptPassword(encrypted: string, key: string): string;
|
|
13
|
+
export declare function isValidEncryptedFormat(encrypted: string): boolean;
|