diff --git a/.obsidian/app.json b/.obsidian/app.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/.obsidian/app.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.obsidian/appearance.json b/.obsidian/appearance.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.obsidian/core-plugins.json b/.obsidian/core-plugins.json new file mode 100644 index 00000000..639b90da --- /dev/null +++ b/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": false, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true, + "webviewer": false +} \ No newline at end of file diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json new file mode 100644 index 00000000..7e4d7651 --- /dev/null +++ b/.obsidian/workspace.json @@ -0,0 +1,192 @@ +{ + "main": { + "id": "74ffcc4b797cf52e", + "type": "split", + "children": [ + { + "id": "ea3b24ec018780b5", + "type": "tabs", + "children": [ + { + "id": "8ff629886042c2ed", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "code/fms/REQUIREMENTS.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "REQUIREMENTS" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "06543d06651622b8", + "type": "split", + "children": [ + { + "id": "599f6f60139dfa56", + "type": "tabs", + "children": [ + { + "id": "4d86de21baabf640", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "文件列表" + } + }, + { + "id": "0e5bc96ba627a8e6", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "搜索" + } + }, + { + "id": "afe47d634e45229e", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "书签" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "58d1d12b71e97cc3", + "type": "split", + "children": [ + { + "id": "21da08dfadda5ac0", + "type": "tabs", + "children": [ + { + "id": "6accf50fd03375de", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "code/fms/REQUIREMENTS.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "REQUIREMENTS 的反向链接列表" + } + }, + { + "id": "65fe03edc1f0032c", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "code/fms/REQUIREMENTS.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "REQUIREMENTS 的出链列表" + } + }, + { + "id": "79c64b5644018587", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "标签" + } + }, + { + "id": "8aa95fa2cbf006aa", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "添加笔记属性" + } + }, + { + "id": "aba1b310fe555cf3", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "code/fms/REQUIREMENTS.md", + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "REQUIREMENTS 的大纲" + } + } + ], + "currentTab": 4 + } + ], + "direction": "horizontal", + "width": 300 + }, + "left-ribbon": { + "hiddenItems": { + "switcher:打开快速切换": false, + "graph:查看关系图谱": false, + "canvas:新建白板": false, + "daily-notes:打开/创建今天的日记": false, + "templates:插入模板": false, + "command-palette:打开命令面板": false, + "bases:新建数据库": false + } + }, + "active": "8ff629886042c2ed", + "lastOpenFiles": [ + "code/fms/REQUIREMENTS.md", + "code/fms/fms-vue/package.json.4230026273", + "code/fms/fms-vue/pnpm-lock.yaml.1103361394" + ] +} \ No newline at end of file diff --git a/code/fms/REQUIREMENTS.md b/code/fms/REQUIREMENTS.md index 39ec34ff..1109ccaf 100644 --- a/code/fms/REQUIREMENTS.md +++ b/code/fms/REQUIREMENTS.md @@ -1,95 +1,83 @@ -# FMS 通用 SQL API 规范 +# FMS 后端 API 需求规范 ## 1. 设计定位 -FMS 采用前端主导、数据库驱动的设计: +FMS 是面向内部用户的多机构 ERP 系统。 -- 前端负责业务流程、页面权限、字段权限和业务校验。 -- 数据库表保存业务数据,数据库视图负责关联、聚合和查询模型。 -- 后端不定义业务实体,不读取表结构,不维护业务接口。 -- 后端只负责参数绑定、SQL 执行、事务控制和统一响应。 -- 数据库约束负责最终的数据完整性。 +当前阶段只确定以下基础原则: -系统只提供两个数据端点: +- 所有 API 请求统一使用 `POST`。 +- 前端登录时通过请求头传递机构码。 +- 一个机构对应一个独立数据库。 +- 登录成功后机构码绑定到访问令牌,后续请求由后端从令牌获取机构码并选择数据库。 +- 前端不接触数据库连接信息。 +- 后端负责身份认证、机构数据库路由和统一响应。 +- 权限编码只用于前端禁用按钮,暂不作为后端业务接口的授权条件。 +- 普通查询和保存采用结构化数据协议,不要求前端编写参数占位符。 +- 高级只读查询可以通过独立的 `load_sql` 接口提交 SQL。 +- 新业务在现有数据库表和视图能够支持的情况下,只需开发前端页面,不需要增加后端业务接口。 -| 方法 | 路径 | 用途 | -|------|------|------| -| `POST` | `/api/query` | 执行单条只读 SQL | -| `POST` | `/api/transaction` | 在同一事务中执行多条 SQL | +当前提供以下接口: -身份认证、业务权限和页面控制不属于本文档范围。 +| 方法 | 路径 | 用途 | 是否需要登录 | +|------|------|------|--------------| +| `POST` | `/api/auth/login` | 登录并获取用户及按钮权限 | 否 | +| `POST` | `/api/data/load` | 通用结构化数据查询 | 是 | +| `POST` | `/api/data/load_sql` | 高级只读 SQL 查询 | 是 | +| `POST` | `/api/data/save` | 通用数据保存 | 是 | -## 2. 通用约定 +## 2. 通用请求规范 -### 2.1 请求格式 +### 2.1 HTTP 方法 + +- 所有接口只使用 `POST`。 +- 不使用 `GET`、`PUT`、`PATCH` 或 `DELETE`。 +- 查询、创建、修改、删除和业务操作都通过后续定义的 `POST` 接口完成。 + +### 2.2 数据格式 - 请求和响应使用 `application/json; charset=utf-8`。 -- SQL 由前端提供。 -- SQL 中的数据值必须使用命名参数,不能直接拼接用户输入。 -- 命名参数格式为 `:参数名`。 -- 参数名格式为 `^[A-Za-z_][A-Za-z0-9_]*$`。 -- 同一个命名参数可以在一条 SQL 中重复使用。 -- SQL 中的表名、字段名、函数、表达式和排序规则由前端直接编写。 +- 除登录路由使用的 `orgid` 请求头外,请求参数放在 JSON 请求体中。 +- 需要登录的接口通过 Bearer Token 认证。 +- 响应不能包含数据库连接信息、令牌密钥、密码或服务端调用栈。 -示例: +需要登录的请求必须包含: -```sql -SELECT id, booking_no, status -FROM v_booking_list -WHERE status = :status - AND created_at >= :startTime -ORDER BY created_at DESC -LIMIT :limit OFFSET :offset +```http +Authorization: Bearer +Content-Type: application/json; charset=utf-8 ``` -对应参数: +### 2.3 命名规范 -```json -{ - "status": "active", - "startTime": "2026-01-01T00:00:00+08:00", - "limit": 20, - "offset": 0 -} +- API 路径、请求字段和响应字段优先使用简短、明确的全小写名称。 +- 能使用单个单词或常用组合词时,不使用驼峰命名法,例如 `username`、`orgid`。 +- 多个单词无法简洁表达时使用 `snake_case`,例如 `access_token`、`display_name`。 +- JSON 字段不使用 `camelCase`,例如不使用 `accessToken`、`displayName`。 +- TypeScript 接口和类型名称遵循语言惯例使用 `PascalCase`,其字段名与 API JSON 字段保持一致。 + +### 2.4 机构标识 + +机构标识统一命名为 `orgid`。 + +规则: + +- `orgid` 类型为非空字符串。 +- 登录请求必须通过 `orgid` 请求头传递机构标识。 +- 登录成功后,`orgid` 保存在访问令牌声明或服务端会话中。 +- 后续业务请求不在请求头或请求体中重复传递 `orgid`。 +- 后端从访问令牌中获取 `orgid` 并选择机构数据库。 +- 前端只传机构码,不能传数据库名称、地址、端口、账号或密码。 +- 机构码必须作为完整字段值传递,不能用于拼接数据库连接字符串或 SQL。 +- 机构码不存在、已停用或未配置数据库时,请求失败。 + +登录请求头示例: + +```http +orgid: SH001 ``` -### 2.2 参数类型 - -参数支持以下 JSON 类型: - -| JSON 类型 | 说明 | -|-----------|------| -| string | 文本、日期时间、UUID、精确小数文本 | -| number | 整数和普通小数 | -| boolean | 布尔值 | -| null | SQL NULL | -| array | 用于 `IN` 等列表参数 | -| object | 用于写入 PostgreSQL JSON/JSONB 字段 | - -数组参数示例: - -```json -{ - "sql": "SELECT * FROM v_booking_list WHERE id IN (:ids)", - "params": { - "ids": [101, 102, 103] - } -} -``` - -数组参数规则: - -- 数组参数在执行时展开为多个绑定参数。 -- 数组元素必须是标量值或 `null`,不能是对象或嵌套数组。 -- 空数组不能用于参数展开;前端应直接调整 SQL 条件。 - -当数据库无法自动确定参数类型时,前端可以在 SQL 中显式转换: - -```sql -WHERE booking_id = CAST(:bookingId AS bigint) -``` - -### 2.3 通用成功响应 +### 2.5 通用成功响应 ```json { @@ -99,14 +87,14 @@ WHERE booking_id = CAST(:bookingId AS bigint) } ``` -### 2.4 通用失败响应 +### 2.6 通用失败响应 ```json { - "code": 1002, - "message": "missing sql parameter", + "code": 1201, + "message": "organization not found", "details": { - "parameter": "status" + "orgid": "SH001" } } ``` @@ -115,58 +103,306 @@ WHERE booking_id = CAST(:bookingId AS bigint) - `code = 0` 表示成功。 - HTTP 状态码表示错误类别,`code` 表示具体错误。 -- `details` 为可选信息。 -- 响应不能包含完整 SQL、数据库连接信息或服务端调用栈。 +- `data` 只在成功且接口有返回数据时出现。 +- `details` 为可选的结构化错误信息。 -## 3. 查询接口 +## 3. 机构数据库路由 -### 3.1 请求 +### 3.1 数据隔离模型 -```http -POST /api/query -Content-Type: application/json +FMS 采用一个机构一个数据库的隔离方式: + +```text +orgid: SH001 -> database: fms_sh001 +orgid: BJ001 -> database: fms_bj001 +orgid: GZ001 -> database: fms_gz001 ``` -请求结构: +上述映射只表示逻辑关系,真实数据库名称和连接信息不能返回给前端。 + +### 3.2 后端处理流程 + +后端收到请求后按以下顺序处理: + +1. 登录时从 `orgid` 请求头获取机构标识;后续请求从访问令牌获取机构标识。 +2. 校验 `orgid` 的格式。 +3. 从服务端机构配置中查找机构。 +4. 检查机构是否启用以及数据库配置是否可用。 +5. 获取该机构对应的数据库连接或连接池。 +6. 在该机构数据库中完成认证或后续业务操作。 +7. 返回统一响应,不暴露数据库连接信息。 + +### 3.3 登录状态与机构绑定 + +- 登录成功后,访问令牌必须绑定当前 `orgid` 和用户标识。 +- 后续请求只以访问令牌绑定的 `orgid` 为准。 +- 后续请求不能通过请求头或请求体覆盖令牌中的 `orgid`。 +- 用户需要切换机构时,必须在登录请求头中传入目标 `orgid` 并重新登录。 +- 当前不提供 refresh;访问令牌过期后需要重新登录。 +- 当前不提供 logout;客户端退出时删除本地访问令牌。 + +### 3.4 连接管理 + +- 数据库连接配置由后端集中管理。 +- 后端可以为每个机构维护独立连接池。 +- 同一次请求只能访问一个机构数据库。 +- 机构数据库不可用时,不能自动切换到其他机构数据库。 +- 日志可以记录机构码,但不能记录数据库密码或完整连接字符串。 + +## 4. 登录接口 + +### 4.1 登录页面 + +登录页面必须提供以下输入项: + +| 输入项 | 必填 | 说明 | +|--------|------|------| +| 机构码 | 是 | 用户所属机构的 `orgid` | +| 用户名 | 是 | 用户登录账号 | +| 密码 | 是 | 用户登录密码 | + +用户点击登录后,前端将机构码放入 `orgid` 请求头,将用户名和密码放入 JSON 请求体。 + +### 4.2 请求 + +```http +POST /api/auth/login +orgid: SH001 +Content-Type: application/json; charset=utf-8 +``` + +请求体: ```json { - "sql": "SELECT id, booking_no, customer_name, total_amount FROM v_booking_list WHERE status = :status ORDER BY created_at DESC LIMIT :limit OFFSET :offset", - "params": { - "status": "active", - "limit": 20, - "offset": 0 + "username": "zhangsan", + "password": "password" +} +``` + +| 字段 | 位置 | 类型 | 必填 | 说明 | +|------|------|------|------|------| +| `orgid` | header | string | 是 | 登录页面填写的机构码,用于选择机构数据库 | +| `username` | body | string | 是 | 登录账号 | +| `password` | body | string | 是 | 登录密码 | + +### 4.3 处理规则 + +- 后端先根据 `orgid` 选择机构数据库,再验证用户名和密码。 +- 登录失败时不能说明账号是否存在。 +- 密码不能写入日志或错误响应。 +- 登录成功后令牌绑定机构码和用户标识。 +- 当前不签发 refresh token。 + +### 4.4 成功响应 + +```json +{ + "code": 0, + "message": "ok", + "data": { + "access_token": "access-token", + "token_type": "Bearer", + "expires_in": 1800, + "organization": { + "orgid": "SH001", + "name": "上海分公司" + }, + "user": { + "id": 1001, + "username": "zhangsan", + "display_name": "张三" + }, + "permissions": [ + "sales.order.create", + "sales.order.approve", + "finance.payment.view" + ] } } ``` +## 5. 前端按钮权限 + +- `permissions` 是当前登录用户拥有的权限编码数组。 +- 权限编码由机构数据库中的用户、角色和权限配置产生。 +- 权限编码建议使用 `<模块>.<资源>.<动作>` 格式。 +- 前端根据权限编码设置按钮的禁用状态。 +- 用户没有对应权限时,按钮保持可见但不可操作。 +- 当前权限只控制前端交互,不作为后端业务授权条件。 + +前端判断示例: + +```typescript +const canApprove = permissions.includes('sales.order.approve') +``` + +## 6. 通用加载接口 + +### 6.1 设计原则 + +- 前端提交数据源、返回字段、过滤条件、排序和分页。 +- 前端不提交 SQL,也不编写参数占位符。 +- 后端根据结构化请求生成查询 SQL,并在后端内部绑定所有数据值。 +- 查询数据源可以是数据库表或视图。 +- 复杂关联、聚合和计算字段优先封装为数据库视图,再通过本接口查询。 +- 后端不为具体业务定义查询接口。 + +### 6.2 请求 + +```http +POST /api/data/load +Authorization: Bearer +Content-Type: application/json; charset=utf-8 +``` + +请求示例: + +```json +{ + "source": "v_sales_order_list", + "fields": [ + "id", + "order_no", + "customer_name", + "status", + "total_amount", + "created_at" + ], + "filter": { + "logic": "and", + "items": [ + { + "field": "status", + "op": "eq", + "value": "pending" + }, + { + "logic": "or", + "items": [ + { + "field": "customer_name", + "op": "contains", + "value": "海运" + }, + { + "field": "order_no", + "op": "starts_with", + "value": "SO2026" + } + ] + } + ] + }, + "sort": [ + { + "field": "created_at", + "direction": "desc" + } + ], + "page": { + "page_no": 1, + "page_size": 20 + }, + "include_total": true +} +``` + | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| -| `sql` | string | 是 | 单条只读 SQL | -| `params` | object | 否 | 命名参数;SQL 无参数时可省略 | +| `source` | string | 是 | 数据库表名或视图名 | +| `fields` | string[] | 是 | 返回字段,必须是非空数组 | +| `filter` | object | 否 | 过滤条件组 | +| `sort` | array | 否 | 排序规则 | +| `page` | object | 否 | 分页;省略时使用默认分页 | +| `include_total` | boolean | 否 | 是否计算符合条件的总记录数,默认 `false` | -### 3.2 SQL 范围 +### 6.3 标识符规则 -查询接口允许: +- `source`、字段名和排序字段只能是数据库标识符,不能包含 SQL 表达式。 +- 标识符必须符合 `^[a-z_][a-z0-9_]*$`。 +- `source` 可以包含一个 schema 前缀,例如 `public.v_sales_order_list`。 +- 带 schema 的数据源必须符合 `.` 格式。 +- 后端必须正确引用标识符,不能把标识符当作数据值拼接。 +- 字段必须真实存在于指定数据源中。 +- `fields` 只返回前端明确请求的字段,不支持 `*`。 -- `SELECT` -- 所有 CTE 都是只读查询的 `WITH` -- 查询表或数据库视图 -- 关联、子查询、窗口函数、聚合和数据库函数 +### 6.4 过滤条件 -查询接口不允许: +过滤条件分为条件项和条件组。 -- `INSERT`、`UPDATE`、`DELETE`、`MERGE` -- `CREATE`、`ALTER`、`DROP`、`TRUNCATE` -- `GRANT`、`REVOKE` -- `CALL`、`DO` 或其他可能修改数据的语句 -- 显式事务控制语句 -- 一次请求执行多条 SQL +条件项: -SQL 末尾可以有一个分号,但不能通过分号附加第二条语句。 -即使最外层是 `SELECT`,查询接口也不能在 CTE 中执行写入。 +```json +{ + "field": "status", + "op": "eq", + "value": "pending" +} +``` -### 3.3 查询响应 +条件组: + +```json +{ + "logic": "and", + "items": [ + { + "field": "status", + "op": "eq", + "value": "pending" + }, + { + "field": "total_amount", + "op": "gte", + "value": 1000 + } + ] +} +``` + +条件组规则: + +- `logic` 只允许 `and` 或 `or`。 +- `items` 必须是非空数组。 +- 条件组可以嵌套条件组。 +- 所有 `value` 都由后端内部参数绑定,不作为 SQL 文本执行。 + +支持的操作符: + +| `op` | 说明 | `value` 规则 | +|------|------|--------------| +| `eq` | 等于 | 单个值 | +| `ne` | 不等于 | 单个值 | +| `gt` | 大于 | 单个值 | +| `gte` | 大于或等于 | 单个值 | +| `lt` | 小于 | 单个值 | +| `lte` | 小于或等于 | 单个值 | +| `in` | 在列表中 | 非空数组 | +| `not_in` | 不在列表中 | 非空数组 | +| `between` | 在范围内,包含边界 | 两个元素的数组 | +| `contains` | 文本包含 | string | +| `starts_with` | 文本开头匹配 | string | +| `ends_with` | 文本结尾匹配 | string | +| `is_null` | 为 `NULL` | 不传 `value` | +| `is_not_null` | 不为 `NULL` | 不传 `value` | + +补充规则: + +- `eq` 的值为 `null` 时按 `is_null` 处理。 +- `ne` 的值为 `null` 时按 `is_not_null` 处理。 +- `contains`、`starts_with` 和 `ends_with` 中的 `%`、`_` 默认作为普通字符处理。 +- 空数组不能用于 `in`、`not_in` 或 `between`。 + +### 6.5 排序和分页 + +- `direction` 只允许 `asc` 或 `desc`。 +- 可以指定多个排序字段,后端按数组顺序生成排序规则。 +- `page_no` 从 1 开始,默认值为 1。 +- `page_size` 默认值为 20,最大值为 5000。 +- 前端需要稳定分页时,应包含主键或其他唯一字段作为最后一个排序字段。 + +### 6.6 成功响应 ```json { @@ -176,216 +412,263 @@ SQL 末尾可以有一个分号,但不能通过分号附加第二条语句。 "rows": [ { "id": 128, - "booking_no": "BK001", + "order_no": "SO20260001", "customer_name": "长荣海运", - "total_amount": 3500 + "status": "pending", + "total_amount": 3500, + "created_at": "2026-07-14T10:30:00+08:00" } ], - "rowCount": 1 + "row_count": 1, + "total": 36, + "page_no": 1, + "page_size": 20 } } ``` +响应规则: + +- `rows` 始终是数组,没有记录时返回空数组。 +- `row_count` 是本次实际返回的记录数。 +- `total` 只在 `include_total = true` 时返回,否则为 `null`。 +- `total` 表示忽略分页后符合过滤条件的记录数。 + +## 7. 高级 SQL 加载接口 + +### 7.1 适用范围 + +`/api/data/load_sql` 用于结构化 `/api/data/load` 无法表达的高级只读查询,例如复杂关联、CTE、窗口函数和聚合查询。 + 规则: -- `rows` 始终是数组。 -- 没有记录时返回空数组。 -- `rowCount` 是本次实际返回的记录数,不代表忽略分页后的总数。 -- 分页、总数和排序均由前端 SQL 决定。 -- 需要总数时可以使用窗口函数或单独执行查询。 +- 该接口是可选的高级能力。 +- 只允许执行单条只读 SQL。 +- 允许 `SELECT`,以及所有 CTE 都是只读查询的 `WITH`。 +- 不允许 `INSERT`、`UPDATE`、`DELETE`、`MERGE`、DDL、权限管理和事务控制语句。 +- 不允许通过分号附加第二条语句。 +- 即使最外层是 `SELECT`,也不允许在 CTE 中写入数据。 +- SQL 不支持参数占位符,前端不能把最终用户输入直接拼接到 SQL 中。 +- 包含页面动态筛选值的查询应优先使用 `/api/data/load`。 +- 返回行数和执行超时遵循普通加载接口限制。 -分页示例: - -```sql -SELECT - v.*, - COUNT(*) OVER () AS total_count -FROM v_booking_list v -WHERE v.status = :status -ORDER BY v.created_at DESC -LIMIT :limit OFFSET :offset -``` - -## 4. 事务接口 - -### 4.1 请求 +### 7.2 请求 ```http -POST /api/transaction -Content-Type: application/json +POST /api/data/load_sql +Authorization: Bearer +Content-Type: application/json; charset=utf-8 ``` -请求结构: +请求示例: ```json { - "operations": [ - { - "sql": "INSERT INTO booking (id, booking_no, customer_id, status) VALUES (:id, :bookingNo, :customerId, :status) RETURNING id", - "params": { - "id": "05f0c596-73d7-4f78-a115-d4591bff6e62", - "bookingNo": "BK001", - "customerId": 42, - "status": "pending" - }, - "expectAffectedRows": 1 - } - ] + "sql": "SELECT customer_id, customer_name, COUNT(*) AS order_count, SUM(total_amount) AS total_amount FROM v_sales_order_list GROUP BY customer_id, customer_name ORDER BY total_amount DESC LIMIT 100" } ``` -事务规则: - -- `operations` 必须是非空数组。 -- 操作按照数组顺序执行。 -- 所有操作使用同一个数据库事务。 -- 任一步骤失败,整个事务回滚。 -- 每个 operation 只能包含一条 SQL。 -- 不允许显式提交、回滚或创建嵌套事务。 - -### 4.2 Operation 结构 - | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| -| `sql` | string | 是 | 单条 SQL | -| `params` | object | 否 | 命名参数 | -| `expectAffectedRows` | integer | 否 | 实际影响行数必须等于该值 | +| `sql` | string | 是 | 单条只读 SQL | -事务接口允许: - -- `SELECT` -- `INSERT` -- `UPDATE` -- `DELETE` -- 上述语句使用的 `WITH` -- `RETURNING` - -事务接口不允许: - -- DDL 语句 -- 权限管理语句 -- 显式事务控制语句 -- `CALL`、`DO` 等不透明过程调用 -- 一次 operation 执行多条 SQL - -### 4.3 主键 - -后端不识别或推断主键。主键字段和值由前端直接写入 SQL 和参数。 - -前端生成 UUID 主键: - -```json -{ - "sql": "INSERT INTO booking (id, booking_no) VALUES (:id, :bookingNo) RETURNING id", - "params": { - "id": "05f0c596-73d7-4f78-a115-d4591bff6e62", - "bookingNo": "BK001" - } -} -``` - -数据库生成主键: - -```json -{ - "sql": "INSERT INTO booking (booking_no) VALUES (:bookingNo) RETURNING id", - "params": { - "bookingNo": "BK001" - } -} -``` - -### 4.4 步骤引用 - -后续步骤的参数可以引用前序步骤返回的数据: - -```json -{ - "operations": [ - { - "sql": "INSERT INTO booking (booking_no, customer_id) VALUES (:bookingNo, :customerId) RETURNING id", - "params": { - "bookingNo": "BK001", - "customerId": 42 - }, - "expectAffectedRows": 1 - }, - { - "sql": "INSERT INTO booking_container (booking_id, container_no) VALUES (:bookingId, :containerNo) RETURNING id", - "params": { - "bookingId": "{{steps.0.rows.0.id}}", - "containerNo": "MSKU001" - }, - "expectAffectedRows": 1 - }, - { - "sql": "INSERT INTO fee (booking_id, fee_type, amount, currency) VALUES (:bookingId, :feeType, :amount, :currency) RETURNING id", - "params": { - "bookingId": "{{steps.0.rows.0.id}}", - "feeType": "ocean_freight", - "amount": 3500, - "currency": "USD" - }, - "expectAffectedRows": 1 - } - ] -} -``` - -引用格式: - -```text -{{steps.<步骤索引>.rows.<记录索引>.<字段名>}} -``` - -规则: - -- 步骤索引和记录索引从 0 开始。 -- 只能引用当前步骤之前的结果。 -- 被引用字段必须由前序 SQL 查询或 `RETURNING` 返回。 -- 占位符必须构成完整参数值,不能嵌入普通字符串。 -- 替换后保留原始数据类型。 -- 引用不存在的步骤、记录或字段时,事务回滚。 -- 步骤引用只能出现在 `params` 中,不能用于动态拼接 SQL。 - -### 4.5 并发控制 - -接口不规定业务并发策略。前端可以通过 SQL 条件和 `expectAffectedRows` 实现乐观锁: - -```json -{ - "sql": "UPDATE booking SET status = :newStatus, version = version + 1 WHERE id = :id AND version = :version RETURNING id, status, version", - "params": { - "id": 128, - "version": 3, - "newStatus": "confirmed" - }, - "expectAffectedRows": 1 -} -``` - -当实际影响行数不等于 `expectAffectedRows` 时,该步骤失败,整个事务回滚。 - -### 4.6 事务响应 +### 7.3 成功响应 ```json { "code": 0, "message": "ok", "data": { - "steps": [ + "rows": [ + { + "customer_id": 42, + "customer_name": "长荣海运", + "order_count": 18, + "total_amount": 63000 + } + ], + "row_count": 1 + } +} +``` + +响应规则: + +- `rows` 始终是数组,没有记录时返回空数组。 +- `row_count` 是本次实际返回的记录数。 +- 排序、分页和总数由 SQL 自身决定。 + +## 8. 通用保存接口 + +### 8.1 设计原则 + +- 保存接口支持在一次请求中新增、修改或删除多条数据。 +- 前端提交目标表、操作类型、数据值和过滤条件。 +- 前端不提交 SQL,也不编写参数占位符。 +- 后端根据结构化请求生成 SQL,并在后端内部绑定所有数据值。 +- 一个请求中的所有变更使用同一个数据库事务。 +- 任一变更失败时,整个请求回滚。 +- 后端不为具体业务定义保存接口。 + +### 8.2 请求 + +```http +POST /api/data/save +Authorization: Bearer +Content-Type: application/json; charset=utf-8 +``` + +请求示例: + +```json +{ + "changes": [ + { + "action": "insert", + "target": "sales_order", + "values": { + "id": "019f62d5-4115-7b22-8c51-8f89e2e8b421", + "order_no": "SO20260001", + "customer_id": 42, + "status": "draft", + "version": 1 + }, + "returning": [ + "id", + "order_no" + ], + "expect_rows": 1 + }, + { + "action": "insert", + "target": "sales_order_item", + "values": { + "id": "019f62d5-4115-79db-ab88-99e8c5dc7f21", + "order_id": "019f62d5-4115-7b22-8c51-8f89e2e8b421", + "product_id": 301, + "quantity": 2, + "unit_price": 1750 + }, + "returning": [ + "id" + ], + "expect_rows": 1 + } + ] +} +``` + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `changes` | array | 是 | 按顺序执行的变更,必须是非空数组 | + +### 8.3 Change 结构 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `action` | string | 是 | `insert`、`update` 或 `delete` | +| `target` | string | 是 | 目标表或可写视图 | +| `values` | object | insert、update 必填 | 要写入的字段和值 | +| `filter` | object | update、delete 必填 | 确定修改或删除范围的过滤条件 | +| `returning` | string[] | 否 | 保存后需要返回的字段 | +| `expect_rows` | integer | 否 | 实际影响行数必须等于该非负整数 | + +### 8.4 保存规则 + +- `target` 和字段名遵循查询接口的标识符规则。 +- `values` 必须是非空对象。 +- `values` 的值可以是 string、number、boolean、null、array 或 object。 +- array 和 object 作为数据库字段值时,由数据库字段类型决定如何转换。 +- `update` 和 `delete` 必须提供非空 `filter`,不允许无条件修改或删除整表数据。 +- 保存过滤条件使用查询接口定义的条件结构和操作符。 +- `returning` 省略时返回空数组。 +- `expect_rows` 不匹配时整个请求失败并回滚。 +- 数据库默认值通过省略对应字段触发。 +- `values` 不支持 SQL 函数、字段引用或其他原始 SQL 表达式。 + +### 8.5 多表保存和主键 + +保存接口不提供步骤结果占位符,也不支持后续变更引用前序变更的返回值。 + +多表新增时,前端应提前生成 UUID 主键,并把主键和外键直接写入各个 change 的 `values`: + +```text +前端生成订单 id +-> 前端把订单 id 写入订单数据 +-> 前端把同一个订单 id 写入明细的 order_id +-> 一次提交所有 changes +-> 后端在同一事务中保存 +``` + +如果某个表必须使用数据库生成的主键,则只能在本次请求的 `returning` 中获取,不能在同一次保存请求的后续 change 中引用。需要多表原子保存时,应优先使用前端生成的 UUID 主键。 + +### 8.6 修改和乐观锁示例 + +```json +{ + "changes": [ + { + "action": "update", + "target": "sales_order", + "values": { + "status": "approved", + "version": 4 + }, + "filter": { + "logic": "and", + "items": [ + { + "field": "id", + "op": "eq", + "value": "019f62d5-4115-7b22-8c51-8f89e2e8b421" + }, + { + "field": "version", + "op": "eq", + "value": 3 + } + ] + }, + "returning": [ + "id", + "status", + "version" + ], + "expect_rows": 1 + } + ] +} +``` + +前端读取当前版本号,保存时同时提交旧版本条件和新版本值。当其他用户已经修改记录时,`expect_rows` 检查失败,整个请求回滚。 + +### 8.7 成功响应 + +```json +{ + "code": 0, + "message": "ok", + "data": { + "results": [ { "index": 0, - "affectedRows": 1, + "action": "insert", + "affected_rows": 1, "rows": [ - { "id": 128 } + { + "id": "019f62d5-4115-7b22-8c51-8f89e2e8b421", + "order_no": "SO20260001" + } ] }, { "index": 1, - "affectedRows": 1, + "action": "insert", + "affected_rows": 1, "rows": [ - { "id": 301 } + { + "id": "019f62d5-4115-79db-ab88-99e8c5dc7f21" + } ] } ] @@ -395,111 +678,193 @@ Content-Type: application/json 响应规则: -- `steps` 顺序与请求中的 `operations` 一致。 +- `results` 顺序与请求中的 `changes` 一致。 - `rows` 始终是数组。 -- `SELECT` 返回查询结果。 -- 带 `RETURNING` 的写入语句返回 `RETURNING` 结果。 -- 不带 `RETURNING` 的写入语句返回空数组。 -- `affectedRows` 表示写入影响行数;纯查询步骤返回 `null`。 +- `affected_rows` 表示该变更实际影响的记录数。 +- 事务提交成功后才返回成功响应。 -## 5. 错误规范 +## 9. 错误规范 | HTTP 状态码 | code | 说明 | |-------------|------|------| | 400 | 1001 | 请求 JSON 或字段结构错误 | -| 400 | 1002 | SQL 参数缺失、重复冲突或存在未使用参数 | -| 400 | 1003 | SQL 为空或包含多条语句 | -| 400 | 1004 | 当前端点不允许该 SQL 类型 | -| 400 | 1005 | 参数类型或数组参数格式错误 | -| 400 | 1006 | SQL 长度、步骤数、参数数或请求大小超限 | -| 409 | 2001 | `expectAffectedRows` 与实际影响行数不一致 | +| 400 | 1006 | 请求大小超限 | +| 401 | 1101 | 机构码、用户名或密码错误 | +| 401 | 1102 | 访问令牌无效或已过期 | +| 404 | 1201 | 机构不存在 | +| 409 | 1202 | 机构已停用 | +| 500 | 1203 | 机构数据库未配置或配置无效 | +| 400 | 1301 | 数据源、目标或字段标识符格式错误 | +| 400 | 1302 | 过滤条件、排序或分页格式错误 | +| 400 | 1303 | 保存变更结构错误 | +| 400 | 1304 | SQL 为空、包含多条语句或不是只读查询 | +| 409 | 2001 | `expect_rows` 与实际影响行数不一致 | | 409 | 2002 | 唯一键、外键、非空或检查约束冲突 | | 409 | 2003 | 死锁、序列化失败或其他并发冲突 | -| 422 | 3001 | SQL 语法错误 | -| 422 | 3002 | 表、视图、字段或函数不存在 | -| 422 | 3003 | 参数值无法转换为数据库字段类型 | -| 422 | 3004 | 步骤引用无法解析 | -| 500 | 9001 | 数据库执行失败 | -| 500 | 9002 | 事务提交失败 | -| 504 | 9003 | SQL 或事务执行超时 | +| 422 | 3001 | 数据源、目标或字段不存在 | +| 422 | 3002 | 数据值无法转换为数据库字段类型 | +| 500 | 3003 | 数据库执行失败 | +| 503 | 9001 | 机构数据库不可用 | +| 500 | 9002 | 服务端内部错误 | +| 504 | 9003 | 查询或保存执行超时 | -事务失败响应: +错误响应不能泄露真实数据库名称、地址、端口、账号、密码或连接字符串。 -```json -{ - "code": 2002, - "message": "database constraint violation", - "details": { - "operationIndex": 1, - "constraint": "booking_container_booking_id_fkey" - } -} -``` +保存请求执行失败时,错误响应的 `details` 应包含失败的 `change_index`。约束冲突可以返回约束名称,但不能返回生成后的完整 SQL。 -规则: - -- 事务执行错误必须返回 `operationIndex`。 -- 可以返回数据库约束名称,但不能返回完整 SQL。 -- 无法分类的数据库错误统一返回 `9001`。 -- 事务失败时所有步骤均视为未提交。 - -## 6. 执行限制 +## 10. 执行限制 默认限制: | 项目 | 限制 | |------|------| -| 单条 SQL 长度 | 100 KB | | 单次请求大小 | 2 MB | -| query 最大返回行数 | 5000 行 | -| transaction 最大步骤数 | 100 个 | -| 单条 SQL 最大参数数 | 5000 个 | -| query 执行超时 | 30 秒 | -| transaction 执行超时 | 60 秒 | +| load 最大返回行数 | 5000 行 | +| load_sql 最大返回行数 | 5000 行 | +| load 最大过滤条件数 | 200 个 | +| save 最大变更数 | 100 个 | +| 单个 change 最大字段数 | 500 个 | +| load 执行超时 | 30 秒 | +| load_sql 执行超时 | 30 秒 | +| save 执行超时 | 60 秒 | -超过返回行数限制时请求失败,不静默截断结果。大数据导出应使用专门的导出机制,不通过本接口一次性返回。 +规则: -## 7. TypeScript 类型参考 +- 超过返回行数限制时请求失败,不静默截断结果。 +- save 超时包含全部变更和事务提交过程。 +- 限制值可以通过后端配置调整。 + +## 11. 当前范围 + +当前文档只定义: + +- 全部请求使用 `POST` 的通用规范。 +- 机构码传递和机构数据库路由规则。 +- 登录接口。 +- 登录响应中的用户信息和前端按钮权限。 +- 通用结构化查询接口。 +- 高级只读 SQL 查询接口。 +- 通用结构化保存接口。 +- 通用响应和基础错误码。 + +当前不提供原有的 `/api/query` 和 `/api/transaction`。数据访问统一使用 `/api/data/load`、`/api/data/load_sql` 和 `/api/data/save`。 + +文件、导入、导出、报表以及页面和菜单加载方式将在后续单独设计。 + +## 12. TypeScript 类型参考 ```typescript -type SqlScalar = string | number | boolean | null -type SqlParam = SqlScalar | SqlScalar[] | Record -type SqlParams = Record - -interface QueryRequest { - sql: string - params?: SqlParams -} - -interface QueryResult> { - rows: T[] - rowCount: number -} - -interface TransactionRequest { - operations: SqlOperation[] -} - -interface SqlOperation { - sql: string - params?: SqlParams - expectAffectedRows?: number -} - -interface TransactionResult { - steps: TransactionStepResult[] -} - -interface TransactionStepResult> { - index: number - affectedRows: number | null - rows: T[] -} - interface ApiResponse { code: number message: string data?: T details?: Record } + +interface LoginRequest { + username: string + password: string +} + +interface LoginResult { + access_token: string + token_type: 'Bearer' + expires_in: number + organization: { + orgid: string + name: string + } + user: { + id: string | number + username: string + display_name: string + } + permissions: string[] +} + +type DataScalar = string | number | boolean | null +type DataValue = DataScalar | DataValue[] | { [key: string]: DataValue } + +type FilterNode = FilterCondition | FilterGroup + +interface FilterCondition { + field: string + op: + | 'eq' + | 'ne' + | 'gt' + | 'gte' + | 'lt' + | 'lte' + | 'in' + | 'not_in' + | 'between' + | 'contains' + | 'starts_with' + | 'ends_with' + | 'is_null' + | 'is_not_null' + value?: DataValue +} + +interface FilterGroup { + logic: 'and' | 'or' + items: FilterNode[] +} + +interface LoadRequest { + source: string + fields: string[] + filter?: FilterGroup + sort?: Array<{ + field: string + direction: 'asc' | 'desc' + }> + page?: { + page_no: number + page_size: number + } + include_total?: boolean +} + +interface LoadResult> { + rows: T[] + row_count: number + total: number | null + page_no: number + page_size: number +} + +interface LoadSqlRequest { + sql: string +} + +interface LoadSqlResult> { + rows: T[] + row_count: number +} + +type SaveAction = 'insert' | 'update' | 'delete' + +interface SaveChange { + action: SaveAction + target: string + values?: Record + filter?: FilterGroup + returning?: string[] + expect_rows?: number +} + +interface SaveRequest { + changes: SaveChange[] +} + +interface SaveResult> { + results: Array<{ + index: number + action: SaveAction + affected_rows: number + rows: T[] + }> +} ``` diff --git a/code/fms/fms-vue/.gitignore b/code/fms/fms-vue/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/code/fms/fms-vue/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/code/fms/fms-vue/.vscode/extensions.json b/code/fms/fms-vue/.vscode/extensions.json new file mode 100644 index 00000000..a7cea0b0 --- /dev/null +++ b/code/fms/fms-vue/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/code/fms/fms-vue/README.md b/code/fms/fms-vue/README.md new file mode 100644 index 00000000..1511959c --- /dev/null +++ b/code/fms/fms-vue/README.md @@ -0,0 +1,5 @@ +# Vue 3 + Vite + +This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` + + diff --git a/code/fms/fms-vue/package.json b/code/fms/fms-vue/package.json new file mode 100644 index 00000000..d016c8a7 --- /dev/null +++ b/code/fms/fms-vue/package.json @@ -0,0 +1,22 @@ +{ + "name": "fms-vue", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.5.39" + }, + "devDependencies": { + "@antdv-next/auto-import-resolver": "^1.1.0", + "@vitejs/plugin-vue": "^6.0.7", + "oxfmt": "^0.58.0", + "unplugin-auto-import": "^21.0.0", + "unplugin-vue-components": "^32.1.0", + "vite": "^8.1.1" + } +} diff --git a/code/fms/fms-vue/pnpm-lock.yaml b/code/fms/fms-vue/pnpm-lock.yaml new file mode 100644 index 00000000..976300de --- /dev/null +++ b/code/fms/fms-vue/pnpm-lock.yaml @@ -0,0 +1,1218 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + vue: + specifier: ^3.5.39 + version: 3.5.39 + devDependencies: + '@antdv-next/auto-import-resolver': + specifier: ^1.1.0 + version: 1.1.0(unplugin-vue-components@32.1.0(rolldown@1.1.5)(vite@8.1.4)(vue@3.5.39)) + '@vitejs/plugin-vue': + specifier: ^6.0.7 + version: 6.0.7(vite@8.1.4)(vue@3.5.39) + oxfmt: + specifier: ^0.58.0 + version: 0.58.0 + unplugin-auto-import: + specifier: ^21.0.0 + version: 21.0.0 + unplugin-vue-components: + specifier: ^32.1.0 + version: 32.1.0(rolldown@1.1.5)(vite@8.1.4)(vue@3.5.39) + vite: + specifier: ^8.1.1 + version: 8.1.4 + +packages: + + '@antdv-next/auto-import-resolver@1.1.0': + resolution: {integrity: sha512-kX8/fYi2YnDJzymiZVqE9cbfL0dmGx23q/qLbVpU3/+/g0005Hd0zWIGyTUdNQDi5ci0i+6P4yOhpTDq47qYUA==} + engines: {node: '>=20.19.0'} + peerDependencies: + unplugin-vue-components: '>=28.0.0' + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oxc-project/types@0.139.0': + resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + + '@oxfmt/binding-android-arm-eabi@0.58.0': + resolution: {integrity: sha512-Uz62sHduGGPftXtILGyxdSW4PX82rUg+rfdNqhsgxe881g4rIoXlIqmZQ6HVKcF4f+F8qMhdD03Bx5u7gmeTdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.58.0': + resolution: {integrity: sha512-rD0lRaJp1b+9vw6X4A2dJWKukd6X8yxiicN4JxXcXayolmUypRZxk+lKR+fVOu5q/iYc0fh5fR4bgmfOfVlbaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.58.0': + resolution: {integrity: sha512-uzbPPk7O6M+w2K65vcQ1woga3wgP8zghjL1KOG5b6qJ8dvYHZJ1VShaslg2KOK6yQIwCQtcMCXqLBM6sqXUNTg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.58.0': + resolution: {integrity: sha512-L0nKYDxU32oxeQqJj21W9SlIMnf81VZEhyah6iDvFhf5q0oynq498Fopth7blErUJVBpVtxQ98RMCfMPqpJX6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.58.0': + resolution: {integrity: sha512-woNwfD58dC5PGS9LSLSD5JYfo/EFK5iG9vhDWkcCg3q78ag7KC8bpDqgvPHrMoXpx83OLXxoSOhu6z8FsVTHlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.58.0': + resolution: {integrity: sha512-Sqs8nMLxuQpY21NKJ1u4stPDmO5hskBCNNh2E3AdCfI1QqWtf4m+Qn4mGEIUO4KGmuq3SWc/SZ80uy5IiwTCDw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.58.0': + resolution: {integrity: sha512-Vd4exzBI5B5hB9m22JiTQzIL23WvHo/Pe+sNXPNeBLXSP9swCBPKCEBRwKpmpQzYhlgYaCgfPcGXPKAJBRIiZQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.58.0': + resolution: {integrity: sha512-bUWi5mHV+4Vi56RLHE1h6q/HHfwAIT3XoB9vJAVeRzfu5NriXM8y6eeJu0vlKa0C9kq2rq1sOWRClhdLHPocrg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.58.0': + resolution: {integrity: sha512-2ZHxemzgHcjtktAuVUwSoyXmGo/t+aF5tS1ciPpPei4rhSyrz3JOqDosXXrmhN/yLUSzJjtuW7ToTWqfQpCj2w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.58.0': + resolution: {integrity: sha512-AwKkVwjVmFQ3bcO7j0McGYAqCKH2a326fswfofng/E8VewCT/raeeGQr4huVhY704deK8AWASSTlxzMj0eZc6Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.58.0': + resolution: {integrity: sha512-xsRpTxfUnJF8D3AUKko/qyWdjw4GZVHlCVFuGlzSCTeewLmykKINW8em1+wx+axsDVtJJcMtvsiaXggXxrlHgw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.58.0': + resolution: {integrity: sha512-Z4AYOTcy7nYEIiXwD62PlerimyYRcfJOgUbQAEBjXz098kxKuERBlRntofGy69HHhe9E0TLVNMl1yspVNu+efw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.58.0': + resolution: {integrity: sha512-A3nhhtZPC/TKVWOPj9q/H3p2znJDCcHWYlJBhWL8hGq/bFmBaNBHC8Np6E581yVq1w9Mi3rMDNzDalWvtUfJtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.58.0': + resolution: {integrity: sha512-2g+tVkgwqphw8R4hgo+kF4oz8+P5RwVOtr9+irsC7uwEp0e9j7Crw8kDGKL20uYlLPD7g02DqA61mC/UNYx98A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.58.0': + resolution: {integrity: sha512-rc15P6AbyyB7426aN8AakLd02Trb3a6ML/mmfAQeVHJEfVofWLcWIrBdy6zDEY+DIaL/s8E4GGPboVw+oP3+EA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.58.0': + resolution: {integrity: sha512-ZWoTM27/HYPOh9iq86DAbhPu9nXb8qKvvGU/h8OfliyVUFAMMNTLDkGsWDKKnDqIkqvZ9+dXlgUOsH1LYO3O7g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.58.0': + resolution: {integrity: sha512-LHZnqFXe2dEfkRI4XdZS/57nEOT/I4UCRX5IyM9v4GYW9XwQCjGe1IUK59SuKw3POwvcgWQ4pme2cYXmNqTNPg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.58.0': + resolution: {integrity: sha512-mZKpg20TpheCJym1rarcZCUJeW1sSruw8zAAaCYWvuVfwIUDN1CXdrPU/JgCWReXTCTrEfCB8Wyo3hh9jSZ2EA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.58.0': + resolution: {integrity: sha512-N/wUU4N5PZ2orBtI+Ko7MnMfYLfE7K91UrGMY/c/pYyHR3lA9kwst1XugkZx+92YcRh/Eo+iv2eTESSWXfiZPA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.5': + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.5': + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.5': + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.5': + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.5': + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.5': + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.5': + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.5': + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@vitejs/plugin-vue@6.0.7': + resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + + '@vue/compiler-core@3.5.39': + resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==} + + '@vue/compiler-dom@3.5.39': + resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==} + + '@vue/compiler-sfc@3.5.39': + resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==} + + '@vue/compiler-ssr@3.5.39': + resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==} + + '@vue/reactivity@3.5.39': + resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==} + + '@vue/runtime-core@3.5.39': + resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==} + + '@vue/runtime-dom@3.5.39': + resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==} + + '@vue/server-renderer@3.5.39': + resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==} + peerDependencies: + vue: 3.5.39 + + '@vue/shared@3.5.39': + resolution: {integrity: sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==} + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + + oxfmt@0.58.0: + resolution: {integrity: sha512-8feG/7NVEHDVwc1OUpP6Pks+TnaDFUw2jLLFIMi5bcmmwxAX2wBQvjSzj62RRTYBf2Op1Wt8xbkmagmPTR5ETg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} + engines: {node: ^10 || ^12 || >=14} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + rolldown@1.1.5: + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + unimport@5.7.0: + resolution: {integrity: sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q==} + engines: {node: '>=18.12.0'} + + unplugin-auto-import@21.0.0: + resolution: {integrity: sha512-vWuC8SwqJmxZFYwPojhOhOXDb5xFhNNcEVb9K/RFkyk/3VnfaOjzitWN7v+8DEKpMjSsY2AEGXNgt6I0yQrhRQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@nuxt/kit': ^4.0.0 + '@vueuse/core': '*' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@vueuse/core': + optional: true + + unplugin-utils@0.3.2: + resolution: {integrity: sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==} + engines: {node: '>=20.19.0'} + + unplugin-vue-components@32.1.0: + resolution: {integrity: sha512-YiUkSxuRjab18XFOrX5VsIxXzccrfmHVGsGeJgSgklb829DQmCy9E4vvDUE4tuvZZdxyFJZX0Oc4TPnnxiiMyg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@nuxt/kit': ^3.2.2 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + + unplugin@3.3.0: + resolution: {integrity: sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: '*' + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + + vite@8.1.4: + resolution: {integrity: sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vue@3.5.39: + resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + +snapshots: + + '@antdv-next/auto-import-resolver@1.1.0(unplugin-vue-components@32.1.0(rolldown@1.1.5)(vite@8.1.4)(vue@3.5.39))': + dependencies: + unplugin-vue-components: 32.1.0(rolldown@1.1.5)(vite@8.1.4)(vue@3.5.39) + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@oxc-project/types@0.139.0': {} + + '@oxfmt/binding-android-arm-eabi@0.58.0': + optional: true + + '@oxfmt/binding-android-arm64@0.58.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.58.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.58.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.58.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.58.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.58.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.58.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.58.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.58.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.58.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.58.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.58.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.58.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.58.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.58.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.58.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.58.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.58.0': + optional: true + + '@rolldown/binding-android-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-x64@1.1.5': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.5': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.5': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.5': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.5': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.5': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.5': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/estree@1.0.9': {} + + '@vitejs/plugin-vue@6.0.7(vite@8.1.4)(vue@3.5.39)': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.1.4 + vue: 3.5.39 + + '@vue/compiler-core@3.5.39': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.39 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.39': + dependencies: + '@vue/compiler-core': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/compiler-sfc@3.5.39': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.39 + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.19 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.39': + dependencies: + '@vue/compiler-dom': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/reactivity@3.5.39': + dependencies: + '@vue/shared': 3.5.39 + + '@vue/runtime-core@3.5.39': + dependencies: + '@vue/reactivity': 3.5.39 + '@vue/shared': 3.5.39 + + '@vue/runtime-dom@3.5.39': + dependencies: + '@vue/reactivity': 3.5.39 + '@vue/runtime-core': 3.5.39 + '@vue/shared': 3.5.39 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.39(vue@3.5.39)': + dependencies: + '@vue/compiler-ssr': 3.5.39 + '@vue/shared': 3.5.39 + vue: 3.5.39 + + '@vue/shared@3.5.39': {} + + acorn@8.17.0: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + confbox@0.1.8: {} + + confbox@0.2.4: {} + + csstype@3.2.3: {} + + detect-libc@2.1.2: {} + + entities@7.0.1: {} + + escape-string-regexp@5.0.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + exsolve@1.1.0: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fsevents@2.3.3: + optional: true + + js-tokens@9.0.1: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + local-pkg@1.2.1: + dependencies: + mlly: 1.8.2 + pkg-types: 2.3.1 + quansync: 0.2.11 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + mlly@1.8.2: + dependencies: + acorn: 8.17.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + + nanoid@3.3.16: {} + + obug@2.1.3: {} + + oxfmt@0.58.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.58.0 + '@oxfmt/binding-android-arm64': 0.58.0 + '@oxfmt/binding-darwin-arm64': 0.58.0 + '@oxfmt/binding-darwin-x64': 0.58.0 + '@oxfmt/binding-freebsd-x64': 0.58.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.58.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.58.0 + '@oxfmt/binding-linux-arm64-gnu': 0.58.0 + '@oxfmt/binding-linux-arm64-musl': 0.58.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.58.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.58.0 + '@oxfmt/binding-linux-riscv64-musl': 0.58.0 + '@oxfmt/binding-linux-s390x-gnu': 0.58.0 + '@oxfmt/binding-linux-x64-gnu': 0.58.0 + '@oxfmt/binding-linux-x64-musl': 0.58.0 + '@oxfmt/binding-openharmony-arm64': 0.58.0 + '@oxfmt/binding-win32-arm64-msvc': 0.58.0 + '@oxfmt/binding-win32-ia32-msvc': 0.58.0 + '@oxfmt/binding-win32-x64-msvc': 0.58.0 + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + pkg-types@2.3.1: + dependencies: + confbox: 0.2.4 + exsolve: 1.1.0 + pathe: 2.0.3 + + postcss@8.5.19: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + quansync@0.2.11: {} + + readdirp@5.0.0: {} + + rolldown@1.1.5: + dependencies: + '@oxc-project/types': 0.139.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.5 + '@rolldown/binding-darwin-arm64': 1.1.5 + '@rolldown/binding-darwin-x64': 1.1.5 + '@rolldown/binding-freebsd-x64': 1.1.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 + '@rolldown/binding-linux-arm64-gnu': 1.1.5 + '@rolldown/binding-linux-arm64-musl': 1.1.5 + '@rolldown/binding-linux-ppc64-gnu': 1.1.5 + '@rolldown/binding-linux-s390x-gnu': 1.1.5 + '@rolldown/binding-linux-x64-gnu': 1.1.5 + '@rolldown/binding-linux-x64-musl': 1.1.5 + '@rolldown/binding-openharmony-arm64': 1.1.5 + '@rolldown/binding-wasm32-wasi': 1.1.5 + '@rolldown/binding-win32-arm64-msvc': 1.1.5 + '@rolldown/binding-win32-x64-msvc': 1.1.5 + + scule@1.3.0: {} + + source-map-js@1.2.1: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinypool@2.1.0: {} + + tslib@2.8.1: + optional: true + + ufo@1.6.4: {} + + unimport@5.7.0: + dependencies: + acorn: 8.17.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + pathe: 2.0.3 + picomatch: 4.0.5 + pkg-types: 2.3.1 + scule: 1.3.0 + strip-literal: 3.1.0 + tinyglobby: 0.2.17 + unplugin: 2.3.11 + unplugin-utils: 0.3.2 + + unplugin-auto-import@21.0.0: + dependencies: + local-pkg: 1.2.1 + magic-string: 0.30.21 + picomatch: 4.0.5 + unimport: 5.7.0 + unplugin: 2.3.11 + unplugin-utils: 0.3.2 + + unplugin-utils@0.3.2: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.5 + + unplugin-vue-components@32.1.0(rolldown@1.1.5)(vite@8.1.4)(vue@3.5.39): + dependencies: + chokidar: 5.0.0 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + obug: 2.1.3 + picomatch: 4.0.5 + tinyglobby: 0.2.17 + unplugin: 3.3.0(rolldown@1.1.5)(vite@8.1.4) + unplugin-utils: 0.3.2 + vue: 3.5.39 + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - unloader + - vite + - webpack + + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.17.0 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + + unplugin@3.3.0(rolldown@1.1.5)(vite@8.1.4): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + rolldown: 1.1.5 + vite: 8.1.4 + + vite@8.1.4: + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.5 + postcss: 8.5.19 + rolldown: 1.1.5 + tinyglobby: 0.2.17 + optionalDependencies: + fsevents: 2.3.3 + + vue@3.5.39: + dependencies: + '@vue/compiler-dom': 3.5.39 + '@vue/compiler-sfc': 3.5.39 + '@vue/runtime-dom': 3.5.39 + '@vue/server-renderer': 3.5.39(vue@3.5.39) + '@vue/shared': 3.5.39 + + webpack-virtual-modules@0.6.2: {} diff --git a/code/fms/fms-vue/public/favicon.svg b/code/fms/fms-vue/public/favicon.svg new file mode 100644 index 00000000..6893eb13 --- /dev/null +++ b/code/fms/fms-vue/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/fms/fms-vue/public/icons.svg b/code/fms/fms-vue/public/icons.svg new file mode 100644 index 00000000..e9522193 --- /dev/null +++ b/code/fms/fms-vue/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/fms/fms-vue/src/App.vue b/code/fms/fms-vue/src/App.vue new file mode 100644 index 00000000..62ebe1b4 --- /dev/null +++ b/code/fms/fms-vue/src/App.vue @@ -0,0 +1,7 @@ + + + diff --git a/code/fms/fms-vue/src/assets/hero.png b/code/fms/fms-vue/src/assets/hero.png new file mode 100644 index 00000000..02251f4b Binary files /dev/null and b/code/fms/fms-vue/src/assets/hero.png differ diff --git a/code/fms/fms-vue/src/assets/vite.svg b/code/fms/fms-vue/src/assets/vite.svg new file mode 100644 index 00000000..5101b674 --- /dev/null +++ b/code/fms/fms-vue/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/code/fms/fms-vue/src/assets/vue.svg b/code/fms/fms-vue/src/assets/vue.svg new file mode 100644 index 00000000..770e9d33 --- /dev/null +++ b/code/fms/fms-vue/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/fms/fms-vue/src/components/HelloWorld.vue b/code/fms/fms-vue/src/components/HelloWorld.vue new file mode 100644 index 00000000..f91553de --- /dev/null +++ b/code/fms/fms-vue/src/components/HelloWorld.vue @@ -0,0 +1,95 @@ + + + diff --git a/code/fms/fms-vue/src/main.js b/code/fms/fms-vue/src/main.js new file mode 100644 index 00000000..2425c0f7 --- /dev/null +++ b/code/fms/fms-vue/src/main.js @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/code/fms/fms-vue/src/style.css b/code/fms/fms-vue/src/style.css new file mode 100644 index 00000000..527d4fba --- /dev/null +++ b/code/fms/fms-vue/src/style.css @@ -0,0 +1,296 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: + rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, 'Segoe UI', Roboto, sans-serif; + --heading: system-ui, 'Segoe UI', Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } +} + +body { + margin: 0; +} + +h1, +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h1 { + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } +} +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +code, +.counter { + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); +} + +code { + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); +} + +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#app { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/code/fms/fms-vue/vite.config.js b/code/fms/fms-vue/vite.config.js new file mode 100644 index 00000000..aa792ec8 --- /dev/null +++ b/code/fms/fms-vue/vite.config.js @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { AntdvNextResolver } from '@antdv-next/auto-import-resolver' +import Components from 'unplugin-vue-components/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue(), + + Components({ + resolvers: [AntdvNextResolver()], + }), + ], +})