diff --git a/.agent/rules/任务与证据.md b/.agent/rules/任务与证据.md new file mode 100644 index 0000000..1991791 --- /dev/null +++ b/.agent/rules/任务与证据.md @@ -0,0 +1,11 @@ +# 任务与证据规则 + +> 权威合同:`docs/系统架构/新版设计/接口契约/任务工具与事件.md`、`模块设计/S02-任务运行.md`;机械门禁:`tests/集成/test_取消恢复与未知调用.py` 等(随 W05/W06/W08 落地)。 + +1. **持久化记账**:任务、步骤、尝试、检查点与有序事件全部落库;禁止以主会话内存当账本。 +2. **冻结输入**:任务创建即冻结来源范围(作品、叙事截止、用途、结构版本)与角色/模型/工具策略副本;执行中不得扩大。 +3. **租约与迟到拒绝**:完成写入校验尝试与租约身份;旧持有者输出不能更新当前候选。 +4. **两种用途不混**:`run_purpose`(production/evaluation/maintenance)与 `content_purpose`(planning/generation/detection/extraction)分别绑定;评测受隔离库权限约束。 +5. **未知调用先对账**:已发出而结果不明的模型调用必须先对账;确定可靠保存才复用,重发是新预算尝试。 +6. **终态明确**:流结束无完成依据、截断、错误、未知用量分别表示;文本片段不是完成事件。 +7. **原文保护**:临时原文先租约后字节,普通清理不得删除持久证据与受控归档;`migrating` 状态不误删。 diff --git a/.agent/rules/元数据消费.md b/.agent/rules/元数据消费.md new file mode 100644 index 0000000..36fc61a --- /dev/null +++ b/.agent/rules/元数据消费.md @@ -0,0 +1,9 @@ +# 元数据消费规则 + +> 权威合同:`docs/系统架构/新版设计/数据模型/元数据与结构演进.md`、`接口契约/元数据投影与版本.md`;机械门禁:`tests/架构/test_元数据不能跨模块写值.py`(随 S04 实现落地)。 + +1. **实例归属业务**:元数据模块拥有模具(类型、字段、结构版本),业务模块拥有实例值;禁止经元数据接口写业务实例。 +2. **稳定 ID**:类型与字段用稳定 ID 登记;禁止传任意表名列名绕过登记。 +3. **只增不改**:作品级结构扩展只追加;已发布字段定义不改语义,退出用弃用标记。 +4. **投影按用途**:表单、提示词、导出各按字段用途投影;收紧投影不删除已存值,未知字段不静默丢弃。 +5. **结构切换并发保护**:结构升级与内容写入并发时以版本约束拒绝冲突;历史值按其当时结构解释。 diff --git a/.agent/rules/审校与修订.md b/.agent/rules/审校与修订.md new file mode 100644 index 0000000..f2278f2 --- /dev/null +++ b/.agent/rules/审校与修订.md @@ -0,0 +1,9 @@ +# 审校与修订规则 + +> 权威合同:`docs/系统架构/新版设计/模块设计/B06-审校修订.md`、`接口契约/正文结构与中文选区.md`;机械门禁:`tests/单元/test_正文格式与选段.py` 等(随 W10/W22/W23 落地)。 + +1. **选区语义**:修改项按段落 ID + 段内码点区间(左闭右开)定位;重复片段不得按首次命中替换;应用前重验原文与哈希。 +2. **不擅动正文形态**:全角标点、异体字、组合字符与有意空白不做自动规范化;规范化必须是显式编辑操作。 +3. **AI 保护区只限自动任务**:保护区不禁止作者本人编辑自己的正文。 +4. **受控修订**:修订包带文档基线、目的与允许范围;有界返修不无限循环,等待作者有明确状态。 +5. **派生失败单独可见**:正文成功后的派生(事实、索引、摘要)失败不回滚正文,也不被静默吞掉。 diff --git a/.agent/rules/文档与资源生成.md b/.agent/rules/文档与资源生成.md new file mode 100644 index 0000000..1fafcc5 --- /dev/null +++ b/.agent/rules/文档与资源生成.md @@ -0,0 +1,10 @@ +# 文档与资源生成规则 + +> 权威合同:`docs/系统架构/新版设计/文件设计/工程配置与部署.md`;机械门禁:`工具/维护索引.py --检查`、`工具/构建资源包.py`。 + +1. **生成物只由登记输入产生**:`src/muse/资源/运行资源.zip` 与 `清单.json` 只从 `工具/资源登记.json` 打包;`docs/接口契约/生成/*` 只从 `工具/生成输入.json` 生成。改生成物先改登记。 +2. **生成物不手改**:`src/muse/资源/`、生成 Schema 与 OpenAPI 是产物;直接编辑会被下次生成覆盖并视为违规。 +3. **索引一致**:所有 `目录.md` 表内相对链接必须可达;技能分组索引与磁盘技能一一对应;`make 检查` 全绿才算文档变更完成。 +4. **资源带发布身份**:资源包必须有 `发布身份` 与逐文件 sha256;生产加载只认安装包内清单,不读未审工作树。 +5. **声明即验证**:写入 README/Makefile/工作流的命令必须先在本地真实执行通过;未验证的命令不得声明。 +6. **单一权威**:正文性定义只在所属权威文档修改;索引、投影和引用不复制第二份定义。 diff --git a/.agent/rules/模块依赖.md b/.agent/rules/模块依赖.md new file mode 100644 index 0000000..b592e4a --- /dev/null +++ b/.agent/rules/模块依赖.md @@ -0,0 +1,13 @@ +# 模块依赖规则 + +> 权威合同:`docs/系统架构/新版设计/总体架构.md`;机械门禁:`tests/架构/test_模块依赖.py` + `make 检查`。 + +1. **业务只经公共接口**:跨模块调用只走目标模块 `接口.py` 暴露的用例;禁止导入其他模块的 `存储.py`、`模型.py` 内部符号。 +2. **共享基础零反向依赖**:`src/muse/共享/`、`配置.py`、`资源加载.py` 禁止导入业务模块。`启动.py` 是组合入口,可以选择并绑定具体实现,但不承载业务规则。 +3. **元数据不写业务值**:S04 元数据只描述、校验和投影结构;业务实例值由拥有它的业务模块写入。 +4. **技能不携带实现**:`.agent/skills/` 只保留方法与操作说明;禁止在技能目录放可执行脚本或 `sys.path` 注入。 +5. **接入不直写表**:HTTP/CLI/主会话接入层禁止手写 SQL;持久化一律走业务模块存储接口。 +6. **宿主不裁决正式内容**:模型宿主(Pi/直接调用)只产候选与证据;作者确认与正式提交不在宿主层发生。 +7. **薄入口**:`python -m muse` 与 CLI 分发入口只做参数解析与转交,不创建连接或写业务逻辑。具名适配器可管理已装配连接的事务范围以调用 S04 等事务参与者;连接凭据、用途和构造仍由基础设施统一处理,禁止手写 SQL。 + +违反任一条:先修依赖方向,再继续;不以"临时导入"放行。 diff --git a/.agent/rules/正式变更.md b/.agent/rules/正式变更.md new file mode 100644 index 0000000..69cf618 --- /dev/null +++ b/.agent/rules/正式变更.md @@ -0,0 +1,9 @@ +# 正式变更规则 + +> 权威合同:`docs/系统架构/新版设计/接口契约/命令与作者决策.md`、`模块设计/S01-正式变更.md`;机械门禁:`tests/架构/test_正式写入入口.py`(随 S01 实现落地)。 + +1. **唯一写入口**:正式内容(正文、事实、规划、设定、决策)只能经 S01 正式变更模块提交;禁止第二条写路径、禁止裸连写库。 +2. **作者确认前置**:候选必须经作者明确确认才能转正;模型与工具没有作者确认身份(`调用身份.作者` 为空的调用禁止写正式内容)。 +3. **携带基线**:每个提交携带正文、候选、结构和来源版本;旧版本请求必须被拒绝且不改动作者新稿。 +4. **原子与幂等**:多参与者提交要么全部生效要么全部回滚;相同 `command_id` + 相同请求幂等重放,携带不同请求必须拒绝。 +5. **失败可见**:提交失败保留作者本地缓冲;服务器回执是已保存的唯一依据,不能用 UI 状态代替。 diff --git a/.agent/rules/测试隔离.md b/.agent/rules/测试隔离.md new file mode 100644 index 0000000..7a00cad --- /dev/null +++ b/.agent/rules/测试隔离.md @@ -0,0 +1,12 @@ +# 测试隔离规则 + +> 权威合同:`docs/系统架构/新版设计/验证设计/测试用例重写.md`、`改造计划/执行与验收.md`;机械门禁:`tests/conftest.py`(OS 进程隔离 + 用例选择 + 全跳过守卫)、`工具/维护索引.py`。 + +1. **默认离线**:默认测试进程由 macOS sandbox 或 Linux seccomp 禁止外部连接,覆盖收集阶段、数据库驱动及其子进程;防护不可用时失败,不退回无防护执行。显式外部用例使用 `--外部环境` 与环境标记选择。 +2. **显式外部环境**:需要隔离 PostgreSQL 的用例标记 `数据库` 并使用 `隔离数据库URL` 夹具;缺 `MUSE_TEST_DATABASE_URL` 时直接失败,不跳过或回退旧库。 + 实际Pi循环另标记`宿主`,通过`make 宿主测试`显式提供固定Node与Pi包路径;普通数据库测试剔除此标记。宿主验证使用合成提供方,不等于真实模型验证。 +3. **用例身份**:完整 `case_id` 与清单中的实际文件、符号及参数行绑定,不靠名称后缀匹配;`pytest --case TC-…` 选择用例,`--case 'TC-…[参数ID]'` 选择参数行。同符号承接多个 ID 时必须分别绑定无交叠参数。未知 ID、目标缺失与冒用均失败;Junit 留存完整 ID 和参数 ID。 +4. **可判定性**:用例体内必须有 assert/raise/fail/skip/xfail;纯 print 用例被索引检查在执行前拦截。 +5. **空收集不通过**:零收集(退出码 5)、全跳过(守卫改写退出码 5)、超时与失败都如实呈现;不得以返回码 0 掩盖。 +6. **替身显式标记**:协议替身、模型替身、宿主替身必须在用例名或文档串标明;替身通过不宣称真实链路通过。 +7. **新旧收集分离**:新用例只放 `tests/单元|契约|集成|架构/`;旧测试树由 `.venv-旧` 与旧 harness 运行,两套互不接管。 diff --git a/.agent/skills/操作/目录.md b/.agent/skills/操作/目录.md new file mode 100644 index 0000000..eeeee6e --- /dev/null +++ b/.agent/skills/操作/目录.md @@ -0,0 +1,7 @@ +| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 | +|------|----------|----------|----------|----------| +| 维护元数据结构 | [SKILL.md](维护元数据结构/SKILL.md) | 查看库内固定结构,或在明确的维护配置中发布内置种子;不替作者启用版本。 | 需要该项操作时 | 只使用已实现的具名入口 | +| 查看与恢复任务 | [SKILL.md](查看与恢复任务/SKILL.md) | 查询持久任务与事件,按作者请求暂停、取消或经登记检查恢复任务。 | 需要该项操作时 | 只使用已实现的具名入口 | +| 决定正文候选去留 | [SKILL.md](决定正文候选去留/SKILL.md) | 比较、修改候选并执行作者明确决定 | 使用新版候选入口时 | 使用被审版本,不自动替作者采纳 | +| 完善故事基础设定 | [SKILL.md](完善故事基础设定/SKILL.md) | 私人探索保存与最终格式检查 | 使用新版探索入口 | 不自动进入正式规划 | +| 确认规划候选 | [SKILL.md](确认规划候选/SKILL.md) | 读取规划候选并执行作者明确决定 | 使用新版规划入口 | 使用被审版本,回查正式回执 | diff --git a/.agent/约束/作者主权.md b/.agent/约束/作者主权.md new file mode 100644 index 0000000..918443c --- /dev/null +++ b/.agent/约束/作者主权.md @@ -0,0 +1,9 @@ +# 作者主权约束 + +> 权威合同:`docs/系统架构/新版设计/接口契约/命令与作者决策.md`;属不可逾越边界。 + +1. **先审后入**:AI 产出的一切正式内容变更先成候选,作者明确确认后才入正典;不存在自动确认。 +2. **作者身份不可伪造**:写入请求的作者身份来自会话与作者动作;模型工具调用没有作者身份,不能代行确认。 +3. **决策可追溯**:每次采纳、拒绝、修改都记录作者、理由与所见版本;撤销与回退走同一审计链。 +4. **作者新稿不受迟到响应影响**:切章、后发请求与旧版本写入不得覆盖作者当前缓冲。 +5. **私人探索边界**:探索稿生命周期独立,不因任何自动化流程被提升为正式内容或被删除。 diff --git a/.agent/约束/凭据与外部调用.md b/.agent/约束/凭据与外部调用.md new file mode 100644 index 0000000..aa50936 --- /dev/null +++ b/.agent/约束/凭据与外部调用.md @@ -0,0 +1,9 @@ +# 凭据与外部调用约束 + +> 权威合同:`docs/系统架构/新版设计/文件设计/后端-基础设施.md`(凭据读取);属不可逾越边界。 + +1. **值不入库不入文**:凭据值不写入源码、文档、测试、日志、错误消息与批次证据;只记获取位置(环境变量名或受控存储路径)。 +2. **受控读取**:运行时凭据经 `src/muse/基础设施/凭据读取.py` 按引用解析;缺引用即失败,不猜测。 +3. **生产不读测试配置**:生产装配不加载 `配置/测试.example.toml` 派生文件;评测与维护用途使用隔离凭据。 +4. **调用留痕**:真实模型调用绑定任务与尝试,保存输入输出证据(脱敏后);凭据在持久化与日志前脱敏。 +5. **离线默认禁外呼**:测试与确定性脚本默认禁止网络、数据库与模型调用;需要时显式标记并使用隔离环境。 diff --git a/.agent/约束/数据与读取范围.md b/.agent/约束/数据与读取范围.md new file mode 100644 index 0000000..cd1f2e7 --- /dev/null +++ b/.agent/约束/数据与读取范围.md @@ -0,0 +1,10 @@ +# 数据与读取范围约束 + +> 权威合同:`docs/系统架构/新版设计/数据模型/存储与对象关系.md`、`改造计划/切换与回退.md`;属不可逾越边界。 + +1. **正式权威**:正式内容以 PostgreSQL 目标库为唯一权威;SQLite 只是运行态账本,冲突以 PG 为准。 +2. **无默认回退**:缺数据库配置即启动失败;禁止回退连接旧正式库(`100.64.0.8:5433/muse-example`)或任何隐式默认库。新代码不得内置旧库连接串。 +3. **旧库冻结**:改造期内旧正式库与 `data/muse.db` 对新实现只读参照;写入与结构变更走 W29/W30 切换流程。 +4. **来源范围冻结**:模型与工具只能读取任务冻结的来源范围(作品、叙事截止、用途、结构版本);不得扩大 `work_id`、`as_of` 或 `purpose`。 +5. **评测隔离**:生产角色无权读取 oracle 与匿名映射,评测角色不得写业务对象。角色任务仍由冻结范围限制读取;评测编排可读取其获准答案,不将答案交给写手或盲评员。数据库角色与任务范围分别承担各自边界。 +6. **参考原文授权**:`data/sources/` 原文只能按授权、租约与归档合同访问;普通清理不得删除持久证据与归档。 diff --git a/.agent/规范/中文正文与界面.md b/.agent/规范/中文正文与界面.md new file mode 100644 index 0000000..f5bc5bc --- /dev/null +++ b/.agent/规范/中文正文与界面.md @@ -0,0 +1,9 @@ +# 中文正文与界面规范 + +> 权威合同:`docs/系统架构/新版设计/接口契约/正文结构与中文选区.md`;机械门禁:`tests/单元/test_正文格式与选段.py`(随 W10 落地)+ `tests/夹具/中文正文.json`。 + +1. **UTF-8 与码点**:正文传输统一 UTF-8;位置按 Unicode 码点左闭右开计数;前端集中转换 UTF-16 偏移。 +2. **不规范化**:全角标点、异体字、组合字符、有意空白与连续空行原样保留;文件名可做 NFC 检查,正文规范化必须是显式编辑操作。 +3. **输入法组合期不提交**:半成文字不进入保存与候选链路。 +4. **界面中文**:作者可见的界面、错误与说明使用简体中文白话;数量与状态来自数据,不写死。 +5. **正文协议权威**:编辑器 HTML 只是显示适配;结构身份、哈希与位置语义以正文协议为准。 diff --git a/.agent/规范/命名.md b/.agent/规范/命名.md new file mode 100644 index 0000000..1ccca12 --- /dev/null +++ b/.agent/规范/命名.md @@ -0,0 +1,11 @@ +# 命名规范 + +> 权威合同:`docs/系统架构/新版设计/项目目录与文件职责.md`(命名与职责节);机械门禁:`make 检查`(索引一致性)。 + +1. **中文优先**:自有业务目录、文件、模块、类、函数与测试名使用中文;技术约定名称(`pyproject.toml`、`uv.lock`、`Makefile`、`conftest.py`、`node_modules` 等)保留英文。 +2. **展示名与稳定身份分离**:对外展示用中文名;稳定身份(case_id、类型 ID、字段 ID、事件类型)用既定英文/十六进制形式,不为改名破坏身份。 +3. **模块文件三件套**:业务模块公开面 = `接口.py`;内部 = `模型.py` + `存储.py`;不新增平行命名。 +4. **测试符号**:`test_<行为名>__`;参数行保留稳定行 ID。 +5. **工具脚本**:`工具/<动词+对象>.py`;登记文件与生成物同名配对(如 `资源登记.json` → `src/muse/资源/`)。 +6. **一个概念一个名**:术语见 `.agent/规范/术语.md`;不为风格引入第二名称。 +7. **React 识别前缀**:组件使用 `Ui` 加中文业务名,Hook 使用 `useMuse` 加中文业务名,保留框架和静态检查所需的英文识别前缀;其余自有业务名仍使用中文。 diff --git a/.agent/规范/术语.md b/.agent/规范/术语.md new file mode 100644 index 0000000..bd9a45b --- /dev/null +++ b/.agent/规范/术语.md @@ -0,0 +1,8 @@ +# 术语规范(执行提示) + +> 权威定义:`docs/系统架构/新版设计/领域模型/术语表.md`。本文件只保留执行提示,不维护第二套定义。 + +1. **使用前先对表**:为业务概念命名或改写文档时,先查术语表;表内已有名称直接用,不造同义词。 +2. **避免使用列**:术语表每条的 _避免使用_(如把参考书称当前作品、把规划称历史事实、把候选称定稿)在代码、文档与提示词中同样适用。 +3. **新旧词不混**:旧实现的英文目录名与旧模块名不进入新代码;引用旧路径时明确标注"旧"。 +4. **读者是作者**:面向作者的说明用白话直陈;"Shadow/Canonical"等内部词在界面与文档中用"候选/正式"。 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6fced45 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.py] +indent_style = space +indent_size = 4 + +[*.{ts,tsx,js,jsx,json,yaml,yml,toml}] +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..348832e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,20 @@ +# 文本换行与差异属性:统一 LF,中文文件名可读 +* text=auto eol=lf + +*.py text eol=lf +*.md text eol=lf +*.json text eol=lf +*.yaml text eol=lf +*.yml text eol=lf +*.toml text eol=lf +*.sql text eol=lf +*.ts text eol=lf +*.tsx text eol=lf + +# 二进制资源 +*.png binary +*.zip binary +*.db binary + +# 语言学上敏感的合并行为:清单与锁文件以本侧为准时人工处理 +uv.lock -merge diff --git a/.github/workflows/构建.yml b/.github/workflows/构建.yml new file mode 100644 index 0000000..4616ea7 --- /dev/null +++ b/.github/workflows/构建.yml @@ -0,0 +1,39 @@ +name: 构建 + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + build: + name: 构建 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + with: + python-version: "3.12" + - uses: pnpm/action-setup@v4 + with: + version: 10.6.4 + - uses: actions/setup-node@v4 + with: + node-version: "22" + cache: pnpm + cache-dependency-path: web/pnpm-lock.yaml + - name: 安装前端依赖 + run: make 前端安装 + - name: 安装依赖 + run: make 安装 + - name: 生成与构建 + run: make 构建 + - name: 干净环境安装验证 + run: | + uv venv /tmp/muse-装验 --python 3.12 + uv pip install --python /tmp/muse-装验/bin/python dist/*.whl + cd / && /tmp/muse-装验/bin/python -c "import muse; from muse.资源加载 import 核对资源; 核对资源()" + - uses: actions/upload-artifact@v4 + with: + name: 发布包 + path: dist/* diff --git a/.github/workflows/检查.yml b/.github/workflows/检查.yml new file mode 100644 index 0000000..5d6ac60 --- /dev/null +++ b/.github/workflows/检查.yml @@ -0,0 +1,60 @@ +name: 检查 + +on: + push: + branches: [main, feat/**] + pull_request: + +jobs: + checks: + name: 检查 + runs-on: ubuntu-latest + services: + postgres: + image: postgres:14 + env: + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_DB: muse_acceptance + ports: ["5432:5432"] + options: >- + --health-cmd "pg_isready -U postgres -d muse_acceptance" + --health-interval 5s --health-timeout 5s --health-retries 10 + env: + MUSE_TEST_DATABASE_URL: postgresql://postgres@127.0.0.1:5432/muse_acceptance + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + with: + python-version: "3.12" + - uses: pnpm/action-setup@v4 + with: + version: 10.6.4 + - uses: actions/setup-node@v4 + with: + node-version: "24.15.0" + cache: pnpm + cache-dependency-path: web/pnpm-lock.yaml + - name: 安装前端依赖 + run: make 前端安装 + - name: 安装依赖 + run: make 安装 + - name: 格式与类型检查 + run: | + make 格式 + make 类型 + - name: 索引一致性 + run: uv run python 工具/维护索引.py --检查 + - name: 前端检查与交互测试 + run: | + make 前端检查 + make 前端测试 + - name: 离线测试 + run: make 测试 + - name: 初始化隔离用途角色 + run: psql "$MUSE_TEST_DATABASE_URL" -v ON_ERROR_STOP=1 -f 数据库/初始化/用途角色.sql + - name: 隔离数据库测试 + run: make 数据库测试 + - name: 固定Pi宿主整链 + run: | + npm install --prefix "$RUNNER_TEMP/muse-pi" --no-save --ignore-scripts @earendil-works/pi-coding-agent@0.84.4 + MUSE_PI_NODE="$(command -v node)" MUSE_PI_PACKAGE="$RUNNER_TEMP/muse-pi/node_modules/@earendil-works/pi-coding-agent" make 宿主测试 diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9f6ea13 --- /dev/null +++ b/Makefile @@ -0,0 +1,73 @@ +# Muse 安装、生成、检查、测试和构建入口。 +# 每个声明命令都必须真实可执行(执行与验收.md:声明命令前验证它真实可执行)。 + +.PHONY: 安装 格式 格式写入 类型 检查 测试 数据库测试 宿主测试 前端安装 前端检查 前端测试 生成 构建 资源核对 + +# 安装:建立并锁定 Python 依赖环境 +安装: + uv sync --group dev + +前端安装: + pnpm --dir web install --frozen-lockfile + +前端检查: + pnpm --dir web run lint + pnpm --dir web run typecheck + pnpm --dir web exec tsc --noEmit --module esnext --moduleResolution bundler --target es2022 --skipLibCheck --typeRoots node_modules/@types --types node ../src/muse/基础设施/宿主/Pi工具桥.ts + +前端测试: + pnpm --dir web run test + +# 格式:静态检查(ruff lint + 格式校验;旧测试树由旧环境维护,不进入范围) +新测试目录 := $(shell for d in tests/单元 tests/契约 tests/集成 tests/架构; do [ -d "$$d" ] && printf '%s ' "$$d"; done) +格式: + uv run ruff check src $(新测试目录) tests/conftest.py 工具 + uv run ruff format --check src $(新测试目录) tests/conftest.py 工具 + +# 格式写入:按项目规则实际格式化 +格式写入: + uv run ruff check --fix src $(新测试目录) tests/conftest.py 工具 + uv run ruff format src $(新测试目录) tests/conftest.py 工具 + +# 类型:pyright 类型检查 +类型: + uv run pyright + +# 检查:格式 + 类型 + 索引核对的统一入口 +检查: 格式 类型 + uv run python 工具/维护索引.py --检查 + +# 测试:默认离线(按标记剔除外部环境用例;未标记用例在 socket 防护下运行) +# 用法:make 测试 全部离线用例 +# make 测试 用例=TC-a5c7d4adafe3 按稳定用例 ID 选择 +测试: + uv run pytest -m "not 数据库 and not 网络 and not 真实模型 and not 浏览器 and not 宿主" $(if $(用例),--case $(用例),) + +# 数据库测试:需要 MUSE_TEST_DATABASE_URL;缺失即失败,不静默跳过 +数据库测试: + @test -n "$$MUSE_TEST_DATABASE_URL" || { echo "缺少 MUSE_TEST_DATABASE_URL(隔离库连接串);拒绝静默跳过" >&2; exit 1; } + uv run pytest --外部环境 -m "数据库 and not 宿主" + +宿主测试: + @test -n "$$MUSE_TEST_DATABASE_URL" -a -n "$$MUSE_PI_NODE" -a -n "$$MUSE_PI_PACKAGE" || { echo "缺少隔离库或固定Pi/Node路径"; exit 1; } + uv run pytest --外部环境 -m 宿主 + +# 迁移:对配置指向的目标库执行显式迁移(维护命令,不在启动时自动执行) +迁移: + uv run python -m muse 迁移 $(配置) + +# 生成:资源包、接口合同与索引(生成物只由登记输入产生) +生成: + uv run python 工具/生成接口.py + cd web && pnpm exec openapi-typescript ../docs/接口契约/生成/openapi.generated.json -o src/接口/生成/类型.ts + pnpm --dir web run build + uv run python 工具/构建资源包.py + uv run python 工具/维护索引.py --写入 + +# 构建:先保证生成物最新,再做可安装包 +构建: 生成 + uv build + +# 资源核对:安装包内资源与清单哈希一致 +资源核对: + uv run python -c "from muse.资源加载 import 核对资源; m = 核对资源(); print('资源清单发布身份:', m['发布身份'], '条目数:', len(m['资源']))" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..15bda3d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,76 @@ +# muse 应用包:安装、命令、依赖与检查配置。 +# 边界:只约束安装、构建或编辑配置;不规范化作者正文、不替代业务权限与验收。 +# 运行时依赖随所属工作包逐步加入(W03 数据库、W06 模型传输、W07 HTTP 等), + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "muse" +version = "0.1.0" +description = "Muse:AI 驱动的长篇创作工具(单作者模块化单体)" +requires-python = ">=3.12" +dependencies = [ + "fastapi>=0.115", + "httpx>=0.28", + "itsdangerous>=2.2", + "jsonschema>=4.23", + "psycopg[binary]>=3.2", + "pydantic>=2.0", + "pyyaml>=6.0", + "uvicorn>=0.30", +] + +[project.scripts] +muse = "muse.__main__:main" + +[dependency-groups] +dev = [ + "pytest>=8.3", + "pytest-timeout>=2.3", + "ruff>=0.8", + "pyright>=1.1.390", +] + +# 资源发布包(运行资源.zip、清单.json、工作台/)是 gitignore 的生成物: +# 存在时打入 sdist/wheel(artifacts 同时作用于两目标),未生成时不阻塞全新环境安装。 +[tool.hatch.build] +artifacts = ["src/muse/资源/**", "!src/muse/资源/迁移/**"] + +[tool.hatch.build.targets.sdist] +include = ["/src", "/数据库/迁移", "/数据库/初始化", "/pyproject.toml", "/uv.lock", "/README.md"] + +[tool.hatch.build.targets.wheel.force-include] +"数据库/迁移" = "muse/资源/迁移" + +[tool.hatch.build.targets.wheel] +packages = ["src/muse"] + +[tool.pytest.ini_options] +# 只收集新版用例目录;旧测试(tests/skills、tests/architecture 英文目录)仍由旧环境 +# (.venv-旧 + 旧 harness)运行,两套收集互不接管。 +testpaths = ["tests/单元", "tests/契约", "tests/集成", "tests/架构"] +# 架构/契约/集成/单元 是目录级组织;完整用例身份绑定登记文件与符号。 +addopts = "--strict-markers --timeout=120" +markers = [ + "数据库: 显式外部执行需要 MUSE_TEST_DATABASE_URL;缺配置失败", + "网络: 需要真实网络访问;必须显式标记", + "真实模型: 需要真实模型调用与预算授权;必须显式标记", + "浏览器: 需要真实浏览器与后端;必须显式标记", +] + +[tool.ruff] +line-length = 100 +target-version = "py312" +src = ["src", "tests", "工具"] + +[tool.ruff.lint] +select = ["E", "F", "W", "I", "B", "UP"] + +[tool.pyright] +venvPath = "." +venv = ".venv" +include = ["src", "工具"] +pythonVersion = "3.12" +typeCheckingMode = "standard" diff --git a/src/muse/__init__.py b/src/muse/__init__.py new file mode 100644 index 0000000..036bb5f --- /dev/null +++ b/src/muse/__init__.py @@ -0,0 +1,8 @@ +"""muse 应用包身份。 + +只承载包版本与身份,不放业务逻辑;业务在各业务模块的 接口.py 暴露。 +""" + +__version__ = "0.1.0" + +__all__ = ["__version__"] diff --git a/src/muse/__main__.py b/src/muse/__main__.py new file mode 100644 index 0000000..6cba083 --- /dev/null +++ b/src/muse/__main__.py @@ -0,0 +1,6 @@ +"""Python 模块入口,与已安装的 muse 命令使用同一 CLI。""" + +from muse.接入.cli.入口 import main + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/muse/共享/__init__.py b/src/muse/共享/__init__.py new file mode 100644 index 0000000..a8d4e55 --- /dev/null +++ b/src/muse/共享/__init__.py @@ -0,0 +1,38 @@ +"""共享基础:少量稳定身份与不可变引用。 + +边界:仅少量不可变基础类型;不导入业务模块、不新增通用业务框架。 +""" + +from muse.共享.时间 import 假时钟, 时钟, 真实时钟 +from muse.共享.标识 import ( + 任务标识, + 作品标识, + 命令标识, + 审阅版本标识, + 标识不合法, + 校验标识, +) +from muse.共享.版本 import 版本引用, 计算内容哈希 +from muse.共享.调用身份 import 内容用途, 用途, 调用身份 +from muse.共享.错误 import Muse错误, 环境缺失错误, 资源缺失错误, 配置错误 + +__all__ = [ + "任务标识", + "作品标识", + "命令标识", + "审阅版本标识", + "标识不合法", + "校验标识", + "内容用途", + "调用身份", + "用途", + "假时钟", + "时钟", + "真实时钟", + "版本引用", + "计算内容哈希", + "Muse错误", + "配置错误", + "环境缺失错误", + "资源缺失错误", +] diff --git a/src/muse/共享/时间.py b/src/muse/共享/时间.py new file mode 100644 index 0000000..1216568 --- /dev/null +++ b/src/muse/共享/时间.py @@ -0,0 +1,63 @@ +"""时钟和期限类型。 + +业务代码只依赖 时钟 协议;测试用 假时钟 固定时间,运行装配注入 真实时钟。 +期限计算只在此处出现一种解释。 +""" + +from __future__ import annotations + +import time +from dataclasses import dataclass, field +from datetime import UTC, datetime, timedelta +from typing import Protocol + + +class 时钟(Protocol): + """给业务与租约用的时钟协议。""" + + def 现在(self) -> datetime: ... + + +class 真实时钟: + """生产用 UTC 时钟。""" + + def 现在(self) -> datetime: + return datetime.now(UTC) + + +@dataclass(slots=True) +class 假时钟: + """测试用可设定的 UTC 时钟;推进时间必须显式调用 推进。""" + + _当前: datetime = field(default_factory=lambda: datetime(2026, 1, 1, tzinfo=UTC)) + + def 现在(self) -> datetime: + return self._当前 + + def 设定(self, 时刻: datetime) -> None: + if 时刻.tzinfo is None: + 时刻 = 时刻.replace(tzinfo=UTC) + self._当前 = 时刻 + + def 推进(self, 时长: timedelta | float) -> None: + """按 timedelta 或秒数前进;不允许倒退(倒退会破坏期限语义)。""" + 增量 = timedelta(seconds=时长) if isinstance(时长, (int, float)) else 时长 + 新时刻 = self._当前 + 增量 + if 新时刻 < self._当前: + raise ValueError("假时钟不允许倒退") + self._当前 = 新时刻 + + +def 截止于(基准: datetime, 时长: timedelta) -> datetime: + """计算期限:基准 + 时长,统一 UTC。""" + if 基准.tzinfo is None: + 基准 = 基准.replace(tzinfo=UTC) + return 基准 + 时长 + + +def 单调秒() -> float: + """单调时钟秒数;用于超时与执行耗时,不受系统改时间影响。""" + return time.monotonic() + + +__all__ = ["时钟", "真实时钟", "假时钟", "截止于", "单调秒"] diff --git a/src/muse/共享/标识.py b/src/muse/共享/标识.py new file mode 100644 index 0000000..15c0e5e --- /dev/null +++ b/src/muse/共享/标识.py @@ -0,0 +1,35 @@ +"""少量稳定身份与不可变引用。 + +身份是跨模块共用的不可变字符串引用;构造时即校验,杜绝空串与空白身份 +进入业务层。这里不生成身份(身份由数据库与业务模块在写入时产生)。 +""" + +from __future__ import annotations + +from typing import NewType + +作品标识 = NewType("作品标识", str) +任务标识 = NewType("任务标识", str) +命令标识 = NewType("命令标识", str) +审阅版本标识 = NewType("审阅版本标识", str) + + +class 标识不合法(ValueError): + """身份为空、空白或包含控制字符。""" + + +def 校验标识(值: str, *, 名称: str = "标识") -> str: + """校验并原样返回身份字符串;不合法即抛 标识不合法。""" + if not isinstance(值, str) or not 值.strip() or any(ord(ch) < 0x20 for ch in 值): + raise 标识不合法(f"{名称}不合法:必须是非空且不含控制字符的字符串") + return 值 + + +__all__ = [ + "作品标识", + "任务标识", + "命令标识", + "审阅版本标识", + "标识不合法", + "校验标识", +] diff --git a/src/muse/共享/版本.py b/src/muse/共享/版本.py new file mode 100644 index 0000000..7fe27dd --- /dev/null +++ b/src/muse/共享/版本.py @@ -0,0 +1,33 @@ +"""revision、内容哈希与版本引用。 + +revision 是单调递增的整数版本号;内容哈希是 UTF-8 字节的 SHA-256 十六进制。 +版本引用在模块间只有一种解释:先比 revision,再核对哈希。 +""" + +from __future__ import annotations + +import hashlib +from dataclasses import dataclass + + +@dataclass(frozen=True, slots=True, order=True) +class 版本引用: + """指向某个对象某个版本的不可变引用。""" + + revision: int + 内容哈希: str + + def __post_init__(self) -> None: + if self.revision < 0: + raise ValueError("revision 不能为负数") + 哈希 = self.内容哈希 + if len(哈希) != 64 or any(ch not in "0123456789abcdef" for ch in 哈希): + raise ValueError("内容哈希必须是 64 位十六进制 SHA-256") + + +def 计算内容哈希(文本: str) -> str: + """按 UTF-8 字节计算 SHA-256 十六进制哈希;正文、规划与快照统一用它。""" + return hashlib.sha256(文本.encode("utf-8")).hexdigest() + + +__all__ = ["版本引用", "计算内容哈希"] diff --git a/src/muse/共享/调用身份.py b/src/muse/共享/调用身份.py new file mode 100644 index 0000000..31f71db --- /dev/null +++ b/src/muse/共享/调用身份.py @@ -0,0 +1,60 @@ +"""作者、任务、用途和授权上下文。 + +合同依据:接口契约/任务工具与事件.md。 +- run_purpose(用途)表示执行隔离:production / evaluation / maintenance。 +- content_purpose(内容用途)表示内容消费:planning / generation / detection / extraction。 +- 两者不能混用:评测写手是 evaluation + generation,按 generation 裁剪字段、 + 受评测数据库权限约束。 +""" + +from __future__ import annotations + +from dataclasses import dataclass +from enum import StrEnum + +from muse.共享.标识 import 任务标识, 校验标识 + + +class 用途(StrEnum): + """执行隔离用途。""" + + 生产 = "production" + 评测 = "evaluation" + 维护 = "maintenance" + + +class 内容用途(StrEnum): + """内容消费用途。""" + + 规划 = "planning" + 生成 = "generation" + 检测 = "detection" + 抽取 = "extraction" + + +@dataclass(frozen=True, slots=True) +class 调用身份: + """一次业务调用携带的稳定身份与用途。 + + 模型工具没有作者确认身份;作者身份只随作者显式动作传入。 + 人工命令的任务引用可空,不虚构一个S02后台任务。 + """ + + 作者: str | None + 任务: 任务标识 | None + 用途: 用途 + 内容用途: 内容用途 + + def __post_init__(self) -> None: + if self.作者 is not None: + 校验标识(self.作者, 名称="作者") + if self.任务 is not None: + 校验标识(self.任务, 名称="任务") + + @property + def 允许写正式内容(self) -> bool: + """只有生产用途且携带作者身份才可能写正式内容(仍需各模块授权检查)。""" + return self.用途 is 用途.生产 and self.作者 is not None + + +__all__ = ["用途", "内容用途", "调用身份"] diff --git a/src/muse/共享/错误.py b/src/muse/共享/错误.py new file mode 100644 index 0000000..9d0c42d --- /dev/null +++ b/src/muse/共享/错误.py @@ -0,0 +1,60 @@ +"""共用错误结构。 + +错误携带稳定错误码、中文说明与可重试性;凭据值禁止进入错误消息与上下文。 +接入层(HTTP/CLI)把这些字段映射为对外错误响应,不另造第二套错误合同。 +""" + +from __future__ import annotations + +from typing import Any + + +class Muse错误(Exception): + """muse 业务与基础设施错误的基类。 + + :param 错误码: 稳定英文代码,接入层据此映射 HTTP 状态与 CLI 退出码。 + :param 说明: 面向作者的中文说明,直陈事实与后果。 + :param 可重试: 该错误在同一输入下重试是否有意义。 + :param 上下文: 排查所需的最小事实;禁止放入凭据值。 + """ + + 错误码: str = "MUSE_ERROR" + 可重试: bool = False + + def __init__(self, 说明: str, *, 上下文: dict[str, Any] | None = None) -> None: + super().__init__(说明) + self.说明 = 说明 + self.上下文: dict[str, Any] = dict(上下文 or {}) + + def 呈现(self) -> dict[str, Any]: + """给接入层与日志的确定性结构;不包含凭据值。""" + return { + "code": self.错误码, + "message": self.说明, + "target_ref": None, + "retryable": self.可重试, + "details": self.上下文, + "actions": [], + } + + +class 配置错误(Muse错误): + """配置缺失、不合法或互相冲突;发生在启动边界,不回退隐式默认。""" + + 错误码 = "MUSE_CONFIG" + + +class 环境缺失错误(Muse错误): + """所需外部环境(隔离库、真实模型授权等)未提供。""" + + 错误码 = "MUSE_ENV_MISSING" + 可重试 = True + + +class 资源缺失错误(Muse错误): + """安装包内带发布身份的资源缺失或哈希不符。""" + + 错误码 = "MUSE_RESOURCE_MISSING" + + +__all__ = ["Muse错误", "配置错误", "环境缺失错误", "资源缺失错误"] diff --git a/src/muse/启动.py b/src/muse/启动.py new file mode 100644 index 0000000..9884212 --- /dev/null +++ b/src/muse/启动.py @@ -0,0 +1,219 @@ +"""选择并注入数据库、宿主、模块和流程实现。 + +合同依据:文件设计/后端-装配与接入.md。 + +- 环境实现的选择集中在此唯一装配位置;更换宿主或连接实现不改业务规则。 +- 只配置和注入模块、宿主、连接、资源与流程;不承载创作规则。 +- 数据库工厂随配置与用途注入,取得连接时才访问外部;模型与角色循环读取任务已绑定配置。 +""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Protocol + +import psycopg + +from muse.上下文.接口 import 上下文服务 +from muse.任务运行.接口 import ( + 任务服务, + 原文服务, + 只读工具集, + 执行上下文, + 模型执行器, + 角色策略目录, + 证据服务, + 调用计价, + 配置版本管理, + 预算管理, + 领取凭证, +) +from muse.作品规划.接口 import 作品服务, 创作进度服务, 探索服务, 登记作品参与者, 规划服务 +from muse.共享.时间 import 时钟, 真实时钟 +from muse.共享.错误 import Muse错误, 配置错误 +from muse.基础设施.受控文件 import 受控文件 +from muse.基础设施.宿主.Pi import Pi宿主 +from muse.基础设施.宿主.直接调用 import 直接宿主, 直接角色循环 +from muse.基础设施.数据库.连接 import 数据库工厂 +from muse.基础设施.模型.HTTP传输 import HTTP传输 +from muse.接入.只读工具 import 领取只读工具 +from muse.故事世界.接口 import 故事服务, 登记故事参与者, 解析本书规划引用 +from muse.正式变更.接口 import 参与者目录, 正式变更服务 +from muse.正文写作.接口 import 正文服务, 登记正文参与者 +from muse.编排.接口 import 流程登记 +from muse.配置 import 应用配置 + + +class 数据库实现(Protocol): + """取得按装配用途绑定的数据库连接。""" + + def 连接(self, *, 只读: bool = False, autocommit: bool = False) -> psycopg.Connection: ... + + def 检查连接(self) -> None: ... + + +class 装配未完成(Muse错误): + """所需实现尚未注入;启动如实失败而不是静默降级。""" + + 错误码 = "MUSE_ASSEMBLY_INCOMPLETE" + + +@dataclass(slots=True) +class 应用装配: + """已注入的实现集合;业务模块只依赖这里的成员。""" + + 配置: 应用配置 + 时钟: 时钟 + 数据库: 数据库实现 | None = None + 任务运行: 任务服务 | None = None + 原文: 原文服务 | None = None + 作品: 作品服务 | None = None + 正文: 正文服务 | None = None + 作品进度: 创作进度服务 | None = None + 故事世界: 故事服务 | None = None + 规划: 规划服务 | None = None + 上下文: 上下文服务 | None = None + + def 要求上下文(self) -> 上下文服务: + if self.上下文 is None: + raise 装配未完成("上下文服务未注入") + return self.上下文 + + def 要求只读工具(self, 领取: 领取凭证) -> 只读工具集: + """工具范围完全取自已保存任务;接入层不重新解释作者、作品或截止。""" + if self.任务运行 is None: + raise 装配未完成("任务运行服务未注入") + return 领取只读工具(self.要求上下文(), self.任务运行, 领取) + + def 要求规划(self) -> 规划服务: + if self.规划 is None: + raise 装配未完成("规划服务未注入") + return self.规划 + + def 要求探索(self) -> 探索服务: + if self.配置.探索草稿 is None: + raise 配置错误("私人探索需要在作者配置中明确探索草稿目录") + return 探索服务(Path(self.配置.探索草稿)) + + def 要求故事世界(self) -> 故事服务: + if self.故事世界 is None: + raise 装配未完成("故事世界服务未注入") + return self.故事世界 + + def 要求作品进度(self) -> 创作进度服务: + if self.作品进度 is None: + raise 装配未完成("创作进度查询未注入") + return self.作品进度 + + def 要求作品(self) -> 作品服务: + if self.作品 is None: + raise 装配未完成("作品服务未注入") + return self.作品 + + def 要求正文(self) -> 正文服务: + if self.正文 is None: + raise 装配未完成("正文服务未注入") + return self.正文 + + def 要求证据(self) -> 证据服务: + return 证据服务(数据库工厂(self.配置.数据库, self.配置.运行用途)) + + def 要求原文(self) -> 原文服务: + if self.原文 is None: + self.原文 = 原文服务( + 数据库工厂(self.配置.数据库, self.配置.运行用途), + 受控文件(Path(self.配置.原文暂存)) if self.配置.原文暂存 else None, + self.时钟, + ) + return self.原文 + + def 要求数据库(self) -> 数据库实现: + if self.数据库 is None: + raise 装配未完成( + "数据库实现未注入(W03 提供);当前构建拒绝启动业务入口", + 上下文={"缺少": "数据库"}, + ) + return self.数据库 + + def 要求模型执行器(self, 上下文: 执行上下文, 计价: 调用计价) -> 模型执行器: + 数据库 = 数据库工厂(self.配置.数据库, self.配置.运行用途) + 版本 = 配置版本管理(数据库).读取任务绑定(上下文.领取.任务ID) + 内容 = 版本.内容 + if 内容.资源发布身份 != self.配置.资源发布身份: + raise 配置错误("任务运行配置与当前安装资源不一致") + if self.任务运行 is None: + raise 装配未完成("任务运行服务未注入") + 当前 = self.任务运行.读取任务(上下文.领取.任务ID) + 步骤 = next(s for s in 当前.流程.步骤 if s.步骤ID == 上下文.领取.步骤ID) + 策略 = 角色策略目录.从发布包() + 角色 = 策略.定义.get("aliases", {}).get(步骤.角色, 步骤.角色) + if 角色 not in 内容.角色配置: + raise 配置错误("任务角色没有对应的固定运行配置") + 声明 = 内容.角色配置[角色] + 提供方 = next(p for p in 内容.提供方 if p.身份 == 声明["provider"]) + 凭据 = next(r for r in 内容.凭据 if r.名称 == 提供方.凭据名称) + return 模型执行器( + self.任务运行, + 预算管理(数据库, 内容.预算策略引用), + self.要求原文(), + self.要求证据(), + 策略, + 直接宿主(HTTP传输(提供方.地址, 凭据.来源, 凭据.位置, 提供方.协议)), + 计价, + 固定配置=版本, + ) + + def 要求角色循环(self, 任务ID: str) -> 直接角色循环 | Pi宿主: + 版本 = 配置版本管理(数据库工厂(self.配置.数据库, self.配置.运行用途)).读取任务绑定(任务ID) + 内容 = 版本.内容 + if 内容.宿主 == "pi": + assert 内容.Node路径 is not None and 内容.Pi包目录 is not None + return Pi宿主(Path(内容.Node路径), Path(内容.Pi包目录), 内容.宿主版本) + if 内容.宿主版本 != "1": + raise 配置错误("未支持的直接宿主合同版本") + return 直接角色循环() + + +def 构建(配置: 应用配置, *, 时钟: 时钟 | None = None, 流程: 流程登记 | None = None) -> 应用装配: + """注入按用途绑定的数据库工厂;装配与自检不建立连接。""" + 数据库 = 数据库工厂(配置.数据库, 配置.运行用途) + 目录 = 参与者目录() + 登记作品参与者(目录, 探索服务(Path(配置.探索草稿)) if 配置.探索草稿 else None, 解析本书规划引用) + 登记正文参与者(目录) + 登记故事参与者(目录) + 正式 = 正式变更服务(数据库, 目录) + 作品 = 作品服务(数据库, 正式) + 正文 = 正文服务(数据库, 正式) + 任务 = 任务服务(数据库, 流程 or 流程登记()) + return 应用装配( + 配置=配置, + 时钟=时钟 or 真实时钟(), + 数据库=数据库, + 任务运行=任务, + 作品=作品, + 正文=正文, + 作品进度=创作进度服务(作品, 正文, 任务), + 故事世界=故事服务(数据库, 正式), + 规划=规划服务(数据库, 正式, 解析本书规划引用), + 上下文=上下文服务(数据库, 任务), + ) + + +def 自检(装配: 应用装配) -> dict[str, Any]: + """进程内自检:报告已注入实现与缺口;不连接任何外部服务。""" + return { + "包版本": __import__("muse").__version__, + "资源发布身份": 装配.配置.资源发布身份, + "数据库": "已注入" if 装配.数据库 is not None else "未注入(W03)", + "宿主": "按任务已绑定配置装配", + } + + +__all__ = [ + "应用装配", + "构建", + "自检", + "装配未完成", + "数据库实现", +] diff --git a/src/muse/资源加载.py b/src/muse/资源加载.py new file mode 100644 index 0000000..5f74004 --- /dev/null +++ b/src/muse/资源加载.py @@ -0,0 +1,165 @@ +"""安装包资源、发布身份与哈希核对。 + +合同依据:接口契约/任务工具与事件.md(生产不读取未审工作树)。 + +- 只读安装包内带发布身份的资源(muse.资源 包内的 清单.json 与运行资源.zip)。 +- 清单与实际字节哈希不符、清单缺失即 资源缺失错误,不加载替代路径。 +- 不读取 Git 工作树、tests 或当前目录;不加载任意用户路径。 +""" + +from __future__ import annotations + +import hashlib +import io +import json +import re +import unicodedata +import zipfile +from importlib import resources +from typing import Any + +import yaml + +from muse.共享.错误 import 资源缺失错误 + +_清单名 = "清单.json" +_包名 = "运行资源.zip" +_资源包 = "muse.资源" + + +def 解析能力声明(数据: bytes) -> tuple[dict, str]: + """构建和运行共同读取frontmatter;正文不夹带登记元信息。""" + try: + 文本 = 数据.decode("utf-8") + 匹配 = re.fullmatch(r"---\r?\n(.*?)\r?\n---(?:\r?\n|$)(.*)", 文本, re.S) + if 匹配 is None: + raise ValueError + 声明 = yaml.safe_load(匹配[1]) + if not isinstance(声明, dict) or not 匹配[2].strip(): + raise ValueError + if not all( + isinstance(声明.get(k), str) and 声明[k].strip() for k in ("id", "name", "description") + ): + raise ValueError + if not re.fullmatch(r"[a-z][a-z0-9_.-]*", 声明["id"]): + raise ValueError + if ( + not re.fullmatch(r"[\u3400-\u9fff]+", 声明["name"]) + or unicodedata.normalize("NFC", 声明["name"]) != 声明["name"] + ): + raise ValueError + if ( + 声明.get("category") not in {"role", "method", "operation"} + or 声明.get("contract_version") != 1 + ): + raise ValueError + 允许字段 = {"id", "name", "category", "contract_version", "description"} + if 声明["category"] == "operation": + 允许字段.add("commands") + 未登记 = sorted(set(声明) - 允许字段) + if 未登记: + # 模型绑定等静态配置不得写进能力文件;角色模型由策略与配置决定。 + raise ValueError(f"能力声明包含未登记字段:{','.join(未登记)}") + return 声明, 匹配[2].strip() + except (UnicodeError, yaml.YAMLError, ValueError) as 错误: + if isinstance(错误, ValueError) and str(错误).startswith("能力声明"): + raise + raise ValueError("能力资源需要稳定身份、中文名称、分类、合同版本和有效正文") from None + + +def 读取能力目录() -> dict: + return json.loads(打开资源("能力/目录.json")) + + +def 读取能力(分类: str, 身份: str, *, 预期哈希: str | None = None) -> dict: + """按稳定身份取得固定角色或Skill,未发布资源不回退到工作树。""" + 条目 = 读取能力目录().get(分类, {}).get(身份) + if 条目 is None: + raise 资源缺失错误("能力身份未在当前安装包登记") + if 预期哈希 is not None and 条目["sha256"] != 预期哈希: + raise 资源缺失错误("能力资源与冻结哈希不符") + 数据 = 打开资源(条目["resource_path"]) + if _sha256_of(数据) != 条目["sha256"]: + raise 资源缺失错误("能力资源与目录摘要不符") + 声明, 正文 = 解析能力声明(数据) + if 声明["id"] != 身份 or 声明["category"] != 分类: + raise 资源缺失错误("能力正文声明与目录身份不一致") + return {**条目, "正文": 正文} + + +def _读取清单字节() -> bytes: + try: + ref = resources.files(_资源包).joinpath(_清单名) + except ModuleNotFoundError as exc: # 资源包尚未生成 + raise 资源缺失错误( + "安装包缺少资源目录;先执行 make 生成", + 上下文={"包": _资源包}, + ) from exc + if not ref.is_file(): + raise 资源缺失错误( + "安装包缺少资源清单;先执行 make 生成", + 上下文={"期望文件": _清单名}, + ) + return ref.read_bytes() + + +def 加载清单() -> dict[str, Any]: + """加载并校验资源清单;返回 清单.json 的确定性结构。""" + 原始 = _读取清单字节() + try: + 清单: dict[str, Any] = json.loads(原始) + except json.JSONDecodeError as exc: + raise 资源缺失错误(f"资源清单不是合法 JSON:{exc}") from exc + if not isinstance(清单.get("发布身份"), str) or not 清单["发布身份"]: + raise 资源缺失错误("资源清单缺少 发布身份") + if not isinstance(清单.get("资源"), dict): + raise 资源缺失错误("资源清单缺少 资源 表") + return 清单 + + +def _sha256_of(数据: bytes) -> str: + return hashlib.sha256(数据).hexdigest() + + +def 打开资源(名称: str) -> bytes: + """按清单打开一个已登记资源的字节;未登记、缺失或哈希不符都失败。""" + 清单 = 加载清单() + 条目 = 清单["资源"].get(名称) + if not isinstance(条目, dict): + raise 资源缺失错误(f"资源未在清单登记:{名称}") + 期望哈希 = str(条目.get("sha256", "")) + if len(期望哈希) != 64: + raise 资源缺失错误(f"资源清单哈希不合法:{名称}") + 资源根 = resources.files(_资源包) + if 名称.startswith(("工作台/", "迁移/")): + 文件 = 资源根.joinpath(名称) + if not 文件.is_file(): + raise 资源缺失错误(f"安装包缺少目录资源:{名称}") + 数据 = 文件.read_bytes() + else: + 压缩包路径 = 资源根.joinpath(_包名) + if not 压缩包路径.is_file(): + raise 资源缺失错误("安装包缺少运行资源包", 上下文={"期望文件": _包名}) + with zipfile.ZipFile(io.BytesIO(压缩包路径.read_bytes())) as zf: + try: + 数据 = zf.read(名称) + except KeyError as exc: + raise 资源缺失错误(f"资源在运行资源包中不存在:{名称}") from exc + 实际哈希 = _sha256_of(数据) + if 实际哈希 != 期望哈希: + raise 资源缺失错误( + f"资源哈希与清单不符:{名称}", + 上下文={"期望": 期望哈希, "实际": 实际哈希}, + ) + return 数据 + + +def 核对资源() -> dict[str, Any]: + """逐项读取并核对包内字节;清单可解析不等同资源完整。""" + 清单 = 加载清单() + for 名称 in 清单["资源"]: + 打开资源(名称) + return 清单 + + +__all__ = ["加载清单", "打开资源", "核对资源", "解析能力声明", "读取能力目录", "读取能力"] diff --git a/src/muse/配置.py b/src/muse/配置.py new file mode 100644 index 0000000..d0749e2 --- /dev/null +++ b/src/muse/配置.py @@ -0,0 +1,147 @@ +"""配置解析、校验与连接引用。 + +合同依据:文件设计/后端-装配与接入.md、工程配置与部署.md。 + +- 解析 TOML 与环境变量,校验后在启动边界暴露缺失与冲突。 +- 只保存连接“引用”(环境变量名或受控存储位置),不保存凭据值。 +- 缺配置或不合法策略抛 配置错误;禁止回退到隐式正式库或示例配置。 +""" + +from __future__ import annotations + +import tomllib +from dataclasses import dataclass, replace +from pathlib import Path +from typing import Any + +from muse.共享.调用身份 import 用途 +from muse.共享.错误 import 配置错误 + +# 凭据引用的取值方式:值本身永远从环境变量或受控存储读取,不落入配置对象。 +_允许的取值方式 = frozenset({"环境变量", "受控存储"}) + + +@dataclass(frozen=True, slots=True) +class 数据库引用: + """数据库连接引用:只记录取值方式与位置,不含连接串明文。""" + + 取值方式: str + 位置: str + + def __post_init__(self) -> None: + if self.取值方式 not in _允许的取值方式: + raise 配置错误("数据库凭据取值方式不合法") + if not self.位置.strip(): + raise 配置错误("数据库凭据位置不能为空") + + +@dataclass(frozen=True, slots=True) +class 服务配置: + """单作者 HTTP 服务的访问来源与凭据位置。""" + + 口令文件: str + 作者ID: str = "author-local" + 监听地址: str = "127.0.0.1" + 端口: int = 8080 + 公开地址: str = "http://127.0.0.1:8080" + 允许来源: tuple[str, ...] = ("http://127.0.0.1:8080",) + + def __post_init__(self) -> None: + if not self.口令文件 or not self.作者ID: + raise 配置错误("HTTP 服务需要作者身份和口令文件引用") + if not 1 <= self.端口 <= 65535: + raise 配置错误("HTTP 端口不合法") + if not self.允许来源 or any(x == "*" for x in self.允许来源): + raise 配置错误("HTTP 写入来源必须明确登记") + + +@dataclass(frozen=True, slots=True) +class 应用配置: + """启动所需的最小配置面;随后续工作包按合同扩展。""" + + 数据库: 数据库引用 + 资源发布身份: str + 运行用途: 用途 = 用途.生产 + HTTP: 服务配置 | None = None + 原文暂存: str | None = None + 探索草稿: str | None = None + + def __post_init__(self) -> None: + if not self.资源发布身份.strip(): + raise 配置错误("资源发布身份不能为空") + + +def _校验数据库节(节: dict[str, Any]) -> 数据库引用: + 取值方式 = 节.get("取值方式") + 位置 = 节.get("位置") + if 取值方式 not in _允许的取值方式: + raise 配置错误( + "数据库.取值方式 不合法", + 上下文={"实际": 取值方式, "允许": sorted(_允许的取值方式)}, + ) + if not isinstance(位置, str) or not 位置.strip(): + raise 配置错误("数据库.位置 不能为空") + return 数据库引用(取值方式=取值方式, 位置=位置) + + +def 解析配置(数据: dict[str, Any]) -> 应用配置: + """从已加载的 TOML 字典解析并校验应用配置;不合法即失败。""" + if "数据库" not in 数据: + raise 配置错误("配置缺少 [数据库] 节") + if "资源" not in 数据 or not isinstance(数据["资源"].get("发布身份"), str): + raise 配置错误("配置缺少 [资源].发布身份") + 发布身份: str = 数据["资源"]["发布身份"] + if not 发布身份.strip(): + raise 配置错误("[资源].发布身份 不能为空") + try: + 运行用途 = 用途(数据.get("运行", {}).get("用途", 用途.生产.value)) + except ValueError: + raise 配置错误("运行.用途 必须为 production、evaluation 或 maintenance") from None + 服务 = 数据.get("HTTP") + try: + HTTP = 服务配置(**服务) if 服务 is not None else None + except TypeError as exc: + raise 配置错误("HTTP 配置字段不合法") from exc + return 应用配置( + 数据库=_校验数据库节(数据["数据库"]), + 资源发布身份=发布身份, + 运行用途=运行用途, + HTTP=HTTP, + 原文暂存=数据.get("文件", {}).get("原文暂存"), + 探索草稿=数据.get("文件", {}).get("探索草稿"), + ) + + +def 读取配置(路径: str | Path) -> 应用配置: + """读取并校验 TOML 配置文件;文件缺失、解析失败与校验失败均为 配置错误。""" + 文件 = Path(路径) + if not 文件.is_file(): + raise 配置错误( + "配置文件不存在", + 上下文={"路径": str(文件), "获取位置": "复制 配置/应用.example.toml 后填写"}, + ) + try: + with 文件.open("rb") as fh: + 数据 = tomllib.load(fh) + except tomllib.TOMLDecodeError as exc: + raise 配置错误(f"配置不是合法 TOML:{exc}", 上下文={"路径": str(文件)}) from exc + 配置 = 解析配置(数据) + if 配置.数据库.取值方式 == "受控存储": + 位置 = Path(配置.数据库.位置) + if not 位置.is_absolute(): + 配置 = replace( + 配置, 数据库=replace(配置.数据库, 位置=str((文件.parent / 位置).resolve())) + ) + if 配置.HTTP is not None and not Path(配置.HTTP.口令文件).is_absolute(): + 配置 = replace( + 配置, + HTTP=replace(配置.HTTP, 口令文件=str((文件.parent / 配置.HTTP.口令文件).resolve())), + ) + if 配置.原文暂存 is not None and not Path(配置.原文暂存).is_absolute(): + 配置 = replace(配置, 原文暂存=str((文件.parent / 配置.原文暂存).resolve())) + if 配置.探索草稿 is not None and not Path(配置.探索草稿).is_absolute(): + 配置 = replace(配置, 探索草稿=str((文件.parent / 配置.探索草稿).resolve())) + return 配置 + + +__all__ = ["数据库引用", "应用配置", "服务配置", "解析配置", "读取配置"] diff --git a/tests/架构/test_技能与文档索引.py b/tests/架构/test_技能与文档索引.py new file mode 100644 index 0000000..2e75971 --- /dev/null +++ b/tests/架构/test_技能与文档索引.py @@ -0,0 +1,423 @@ +"""技能与文档索引行为验证。 + +保护:技能目录合同(SKILL.md 完整、名称唯一且全中文、frontmatter 一致) +与活跃文档索引(目录.md 标准索引、链接可达)。 + +已实现子集(W02):目录与链接检查、技能目录基础合同。 +依赖技能承接(W13+)、角色目录(W06)与旧库参数守卫(W03/W07)的用例 +在对应包落地后逐例实现。 +""" + +from __future__ import annotations + +import sys +import unicodedata +from pathlib import Path + +import pytest + +仓库根 = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(仓库根 / "工具")) +from 维护索引 import 检查技能目录, 检查目录链接 # noqa: E402 + + +def _写能力样本(根: Path, 相对: str, 身份: str, 名称: str) -> Path: + import yaml + + 文件 = 根 / 相对 + 文件.parent.mkdir(parents=True, exist_ok=True) + 声明 = { + "id": 身份, + "name": 名称, + "category": "method", + "contract_version": 1, + "description": "合成方法", + } + 文件.write_text( + "---\n" + yaml.safe_dump(声明, allow_unicode=True) + "---\n\n只提出有依据的规划方法。\n" + ) + return 文件 + + +def test_passing_project_is_independent_of_current_repository__a189e4(tmp_path, monkeypatch): + from 构建资源包 import 编译能力资源 + + 根 = tmp_path / "独立项目" + 文件 = _写能力样本(根, ".agent/skills/alpha/SKILL.md", "alpha", "合成方法") + monkeypatch.chdir(tmp_path) + 目录, 资源 = 编译能力资源(根, [文件.relative_to(根).as_posix()], set()) + assert set(目录["method"]) == {"alpha"} + assert len(资源) == 1 and 资源["能力/method/alpha.md"] == 文件.read_bytes() + + +def test_nested_skill_path_is_accepted_from_manifest__c9cb85(tmp_path): + from 构建资源包 import 编译能力资源 + + 相对 = ".agent/skills/planning/alpha/SKILL.md" + 文件 = _写能力样本(tmp_path, 相对, "alpha", "合成方法") + 目录, 资源 = 编译能力资源(tmp_path, [相对], set()) + assert 目录["method"]["alpha"]["resource_path"] == "能力/method/alpha.md" + assert 资源["能力/method/alpha.md"] == 文件.read_bytes() + + +def test_chinese_only_policy_rejects_english_manifest_name__c089ce(tmp_path): + from 构建资源包 import 编译能力资源 + + _写能力样本(tmp_path, "SKILL.md", "alpha", "alpha") + with pytest.raises(ValueError, match="中文名称"): + 编译能力资源(tmp_path, ["SKILL.md"], set()) + + +def test_chinese_only_policy_allows_new_native_chinese_skill__b72bcc(tmp_path): + from 构建资源包 import 编译能力资源 + + a = _写能力样本(tmp_path, "规划/规划作品/SKILL.md", "plan-work", "规划作品") + b = _写能力样本(tmp_path, "写作/新增技能/SKILL.md", "new-method", "新增技能") + 目录, 资源 = 编译能力资源(tmp_path, [p.relative_to(tmp_path).as_posix() for p in (a, b)], set()) + assert {v["name"] for v in 目录["method"].values()} == {"规划作品", "新增技能"} + assert len(资源) == 2 + + +def test_non_normalized_skill_name_is_rejected__5b50cc(tmp_path): + from 构建资源包 import 编译能力资源 + + _写能力样本(tmp_path, "SKILL.md", "alpha", "e\u0301-name") + with pytest.raises(ValueError, match="中文名称"): + 编译能力资源(tmp_path, ["SKILL.md"], set()) + + +def test_skill_name_with_decorative_punctuation_is_rejected__984f56(tmp_path): + from 构建资源包 import 编译能力资源 + + _写能力样本(tmp_path, "SKILL.md", "story", "故事结构!") + with pytest.raises(ValueError, match="中文名称"): + 编译能力资源(tmp_path, ["SKILL.md"], set()) + + +def test_资源按稳定身份编译且移动不改变身份__a66001(tmp_path): + from 构建资源包 import 编译能力资源 + + 文件 = tmp_path / "初始.md" + 文件.write_text( + "---\nid: writer\nname: 写手\ncategory: role\ncontract_version: 1\n" + "description: 合成职责\n---\n\n只执行固定任务。\n" + ) + 目录, 资源 = 编译能力资源(tmp_path, ["初始.md"], set()) + assert 目录["role"]["writer"]["name"] == "写手" + assert 资源[目录["role"]["writer"]["resource_path"]] == 文件.read_bytes() + 文件.rename(tmp_path / "迁移.md") + 新目录, _ = 编译能力资源(tmp_path, ["迁移.md"], set()) + assert 新目录["role"]["writer"]["sha256"] == 目录["role"]["writer"]["sha256"] + + +def test_技能只声明已实现命令且重复身份拒绝__a66002(tmp_path): + import pytest + + from 构建资源包 import 编译能力资源 + + 文件 = tmp_path / "SKILL.md" + 文件.write_text( + "---\nid: inspect-task\nname: 查看任务\ncategory: operation\ncontract_version: 1\n" + "description: 合成操作\ncommands: [read_task]\n---\n\n按授权读取任务。\n" + ) + with pytest.raises(ValueError, match="命令"): + 编译能力资源(tmp_path, ["SKILL.md"], set()) + 目录, _ = 编译能力资源(tmp_path, ["SKILL.md"], {"read_task"}) + assert 目录["operation"]["inspect-task"]["commands"] == ["read_task"] + (tmp_path / "副本.md").write_bytes(文件.read_bytes()) + with pytest.raises(ValueError, match="重复"): + 编译能力资源(tmp_path, ["SKILL.md", "副本.md"], {"read_task"}) + + +def test_运行按发布目录读取角色正文和命令__a66003(): + import pytest + + from muse.任务运行.接口 import 角色策略目录 + from muse.共享.错误 import 资源缺失错误 + from muse.资源加载 import 读取能力, 读取能力目录 + + 目录 = 读取能力目录() + assert set(目录["role"]) == {"writer", "planner", "extractor", "detector", "judge"} + 角色 = 读取能力("role", "writer", 预期哈希=目录["role"]["writer"]["sha256"]) + assert 角色["name"] == "写手" and "生成阶段" in 角色["正文"] + assert not 角色["正文"].startswith("---") + 策略 = 角色策略目录.从发布包() + assert 策略.读取角色提示("writer") == 角色["正文"] + assert len(策略.资源发布身份) == 64 + 操作 = 读取能力("operation", "inspect-task") + assert "control_task" in 操作["commands"] + with pytest.raises(资源缺失错误): + 读取能力("role", "writer", 预期哈希="0" * 64) + + +def _建技能( + 根: Path, 分组: str, 名称: str, *, front名: str | None = None, 正文: str | None = None +) -> Path: + 目录 = 根 / ".agent" / "skills" / 分组 / 名称 + 目录.mkdir(parents=True, exist_ok=True) + if 正文 is None: + 有效名 = front名 if front名 is not None else 名称 + 正文 = f"---\nname: {有效名}\ndescription: 合成技能说明\n---\n\n# {名称}\n\n方法正文。\n" + (目录 / "SKILL.md").write_text(正文, encoding="utf-8") + return 目录 + + +def test_agent_content_directories_have_standard_indexes__b35be1() -> None: + """一级内容目录可从根索引发现,且使用固定五列表头。""" + 根 = 仓库根 / ".agent" + 根索引 = (根 / "目录.md").read_text() + 表头 = "| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 |" + for 目录 in sorted(p for p in 根.iterdir() if p.is_dir()): + 索引 = 目录 / "目录.md" + assert 索引.is_file(), f"缺少一级目录索引:{目录.name}" + assert 索引.read_text().startswith(表头), f"未使用固定五列表头:{索引}" + assert f"{目录.name}/目录.md" in 根索引, f"一级目录不可发现:{目录.name}" + + +def test_active_markdown_links_resolve__3734a6() -> None: + """活跃执行知识(.agent 树)的 Markdown 相对链接可达。""" + 问题: list[str] = [] + import re + + 链接 = re.compile(r"\[[^\]]*\]\(([^)#\s]+)[)#]") + for 文件 in (仓库根 / ".agent").rglob("*.md"): + if "__pycache__" in 文件.parts: + continue + base = 文件.parent + for 序, 行 in enumerate(文件.read_text(encoding="utf-8").splitlines(), 1): + if 行.strip().startswith("|"): + continue # 目录表行由 检查目录链接 覆盖 + for 地址 in 链接.findall(行): + if 地址.startswith(("http://", "https://", "mailto:")): + continue + if not (base / 地址).exists(): + 问题.append(f"{文件.relative_to(仓库根)}:{序} 链接不存在:{地址}") + assert 问题 == [], "\n".join(问题[:10]) + + +def test_skill_directory_without_skill_file_fails__e8450b(tmp_path: Path) -> None: + """技能目录缺少 SKILL.md 时被报告。""" + 目录 = tmp_path / ".agent" / "skills" / "写作" / "无文件技能" + 目录.mkdir(parents=True) + (目录 / "references.md").write_text("只有资料", encoding="utf-8") + 问题 = 检查技能目录(tmp_path) + assert any("缺少 SKILL.md" in p for p in 问题), 问题 + + +def test_empty_skill_file_is_reported__1d2b76(tmp_path: Path) -> None: + """空 SKILL.md 被报告,不静默放行。""" + _建技能(tmp_path, "写作", "空技能", 正文="---\n") + 问题 = 检查技能目录(tmp_path) + assert any("为空" in p for p in 问题), 问题 + + +def test_frontmatter_name_must_match_skill_directory__703c7b(tmp_path: Path) -> None: + """frontmatter name 必须与技能目录名一致。""" + _建技能(tmp_path, "写作", "真名技能", front名="别名技能") + 问题 = 检查技能目录(tmp_path) + assert any("name 与目录不一致" in p for p in 问题), 问题 + + +def test_duplicate_frontmatter_names_are_reported__eb80b5(tmp_path: Path) -> None: + """同名技能(跨分组)被报告。""" + _建技能(tmp_path, "写作", "重复技能") + _建技能(tmp_path, "规划", "重复技能") + 问题 = 检查技能目录(tmp_path) + assert any("重复" in p for p in 问题), 问题 + + +def test_chinese_skill_name_is_accepted__de3efe(tmp_path: Path) -> None: + """全中文技能名通过全部目录合同。""" + _建技能(tmp_path, "写作", "中文技能") + assert 检查技能目录(tmp_path) == [] + + +def test_chinese_only_policy_rejects_english_skill_name__d5c460(tmp_path: Path) -> None: + """英文技能名被中文唯一政策拒绝。""" + _建技能(tmp_path, "写作", "english-skill") + 问题 = 检查技能目录(tmp_path) + assert any("全中文" in p for p in 问题), 问题 + + +def test_non_normalized_skill_name_is_rejected__a87a41(tmp_path: Path) -> None: + """非 NFC 规范形式的技能名被拒绝。""" + 非规范 = "组合e\u0301音技能" # e + 组合变音符:NFC 会合成 é,与原名不同 + assert unicodedata.normalize("NFC", 非规范) != 非规范 + _建技能(tmp_path, "写作", 非规范) + 问题 = 检查技能目录(tmp_path) + assert any("NFC" in p for p in 问题), 问题 + + +def test_skill_name_with_whitespace_is_rejected__b68ab9(tmp_path: Path) -> None: + """含空白的技能名被拒绝。""" + _建技能(tmp_path, "写作", "带 空白技能") + 问题 = 检查技能目录(tmp_path) + assert any("含空白" in p for p in 问题), 问题 + + +def test_missing_skills_directory_fails_closed__5acb68(tmp_path: Path) -> None: + """技能根目录缺失时明确失败,不静默跳过。""" + 问题 = 检查技能目录(tmp_path) + assert any("不存在" in p for p in 问题), 问题 + + +def test_index_matches_disk_and_manifest__000974() -> None: + """每个分组的 目录.md 索引与磁盘上的技能目录一一对应(不多不少)。""" + import re + + 技能根 = 仓库根 / ".agent" / "skills" + 链接 = re.compile(r"\]\(([^/]+)/SKILL\.md\)") + for 分组 in sorted(p for p in 技能根.iterdir() if p.is_dir()): + 目录文件 = 分组 / "目录.md" + assert 目录文件.is_file(), f"分组缺目录:{分组.name}" + 索引名 = set(链接.findall(目录文件.read_text(encoding="utf-8"))) + 磁盘名 = {p.name for p in 分组.iterdir() if p.is_dir() and (p / "SKILL.md").is_file()} + assert 索引名 == 磁盘名, ( + f"分组 {分组.name} 索引与磁盘不一致:" + f"索引多出 {sorted(索引名 - 磁盘名)},磁盘多出 {sorted(磁盘名 - 索引名)}" + ) + + +def test_仓库当前树通过目录链接检查__b7c8d9() -> None: + """当前仓库 目录.md 索引链接全部可达。""" + assert 检查目录链接(仓库根) == [] + + +# --------------------------------------------------------------------------- +# W06 承接:能力目录与角色合同(原 SkillCatalogTest) +# --------------------------------------------------------------------------- + + +def _角色字节(身份: str, 名称: str, 额外: str = "") -> bytes: + return ( + f"---\nid: {身份}\nname: {名称}\ncategory: role\n" + f"contract_version: 1\ndescription: 合成角色\n{额外}---\n\n你是{名称},按合同执行。\n" + ).encode() + + +def test_current_role_catalog_is_exact_and_host_neutral__569f09() -> None: + """已发布角色目录恰好五类且宿主中立:frontmatter 不携带任何模型绑定。""" + from muse.资源加载 import 解析能力声明, 读取能力目录 + + 目录 = 读取能力目录()["role"] + assert set(目录) == {"writer", "planner", "extractor", "detector", "judge"} + from muse.资源加载 import 打开资源 + + for 身份, 条目 in 目录.items(): + 声明, 正文 = 解析能力声明(打开资源(条目["resource_path"])) + assert 声明["id"] == 身份 and 声明["category"] == "role" + assert "model" not in 声明 and "provider" not in 声明, f"{身份} 携带静态模型绑定" + assert 正文.strip() + + +def test_role_catalog_ignores_directory_index__4da912(tmp_path: Path) -> None: + """能力目录只收登记文件;目录索引不是能力,不进入发布包。""" + import sys as _sys + + _sys.path.insert(0, str(仓库根 / "工具")) + from 构建资源包 import 编译能力资源 + + 角色 = ("writer", "planner", "extractor", "detector", "judge") + 名称 = { + "writer": "写手", + "planner": "规划员", + "extractor": "知识抽取员", + "detector": "检测员", + "judge": "评委", + } + for r in 角色: + (tmp_path / "角色").mkdir(exist_ok=True) + (tmp_path / "角色" / f"{名称[r]}.md").write_bytes(_角色字节(r, 名称[r])) + (tmp_path / "角色" / "目录.md").write_text( + "| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 |\n" + "|------|----------|----------|----------|----------|\n", + encoding="utf-8", + ) + 目录, 资源 = 编译能力资源(tmp_path, [f"角色/{名称[r]}.md" for r in 角色], set()) + assert set(目录["role"]) == set(角色) + assert len(资源) == 5 # 索引文件未被扫描进包 + + +def test_role_contract_is_central_and_complete__a74ee6() -> None: + """角色合同与策略是唯一中心:五角色、固定模型策略与正文提示都齐备。""" + import yaml + + from muse.任务运行.角色策略 import 角色策略目录 + from muse.资源加载 import 读取能力目录 + + 定义 = yaml.safe_load((仓库根 / "配置" / "角色策略.yaml").read_text(encoding="utf-8")) + 策略 = 角色策略目录(定义, 角色资源=读取能力目录()["role"]) + 合同 = (仓库根 / "muse" / "sot" / "角色合同.md").read_text(encoding="utf-8") + for 角色, 中文名 in { + "writer": "网文写手", + "detector": "检测员", + "judge": "质量评委", + "extractor": "知识抽取员", + }.items(): + assert 角色 in 合同 and 中文名 in 合同 + for 身份 in ("writer", "planner", "judge"): + assert 定义["roles"][身份]["model_policy"] == "fixed" + for 别名, 正名 in 定义.get("aliases", {}).items(): + assert 策略.读取角色提示(别名) == 策略.读取角色提示(正名) + + +def test_role_frontmatter_rejects_static_model_binding__7d0984() -> None: + """角色 frontmatter 出现 model 等未登记字段直接拒绝。""" + from muse.资源加载 import 解析能力声明 + + with pytest.raises(ValueError, match="未登记字段"): + 解析能力声明(_角色字节("writer", "写手", 额外="model: claude-opus\n")) + with pytest.raises(ValueError, match="未登记字段"): + 解析能力声明(_角色字节("planner", "规划员", 额外="provider: primary\n")) + + +def test_current_catalog_is_valid__0af776() -> None: + """发布目录内每个能力都能按登记哈希读取且声明与目录一致。""" + from muse.资源加载 import 读取能力, 读取能力目录 + + 目录 = 读取能力目录() + 总数 = sum(len(v) for v in 目录.values()) + assert 总数 > 0 + for 分类, 条目组 in 目录.items(): + for 身份, 条目 in 条目组.items(): + 资源 = 读取能力(分类, 身份, 预期哈希=条目["sha256"]) + assert 资源["name"] == 条目["name"] + assert 资源["正文"].strip() + + +def test_directory_and_name_must_match__29d9f3(tmp_path: Path) -> None: + """能力身份由声明而非文件位置决定:入包路径从身份派生,声明与目录一致。""" + import sys as _sys + + _sys.path.insert(0, str(仓库根 / "工具")) + from muse.资源加载 import 解析能力声明 + from 构建资源包 import 编译能力资源 + + # 源文件名与中文名无关;入包后由身份定位,不再允许目录名充当身份。 + (tmp_path / "旧位置").mkdir() + 源 = tmp_path / "旧位置" / "legacy-plan.md" + 源.write_bytes(_角色字节("writer", "写手")) + 目录, 资源 = 编译能力资源(tmp_path, ["旧位置/legacy-plan.md"], set()) + assert "能力/role/writer.md" in 资源 + 声明, _ = 解析能力声明(资源["能力/role/writer.md"]) + assert (声明["id"], 声明["name"]) == ("writer", "写手") + assert 目录["role"]["writer"]["resource_path"] == "能力/role/writer.md" + + +def test_chinese_skill_name_is_accepted__16a67a() -> None: + """中文名称的能力声明被接受并原样保留。""" + from muse.资源加载 import 解析能力声明 + + 声明, 正文 = 解析能力声明(_角色字节("writer", "写手")) + assert 声明["name"] == "写手" + assert "按合同执行" in 正文 + + +def test_unknown_frontmatter_key_is_rejected__432390() -> None: + """未知 frontmatter 键被拒绝;合法键集内仍通过。""" + from muse.资源加载 import 解析能力声明 + + with pytest.raises(ValueError, match="未登记字段"): + 解析能力声明(_角色字节("judge", "评委", 额外="temperature: 0.7\n")) + 声明, _ = 解析能力声明(_角色字节("judge", "评委")) + assert set(声明) == {"id", "name", "category", "contract_version", "description"} diff --git a/tests/架构/test_模块依赖.py b/tests/架构/test_模块依赖.py new file mode 100644 index 0000000..72fdb7b --- /dev/null +++ b/tests/架构/test_模块依赖.py @@ -0,0 +1,330 @@ +"""模块依赖行为验证。 + +保护:安装产物边界与依赖方向——业务只经公共接口、Skill 不携带实现、 +接入不直写表、共享基础不反向依赖业务、装配层不承载创作规则。 + +已实现子集(W02): +- 安装包在任意 cwd 可导入且导入不产生外部连接(含旧树同名包遮挡守卫); +- 技能目录不注入 sys.path、不携带实现脚本; +- src/muse 活跃运行代码不依赖外部专有 CLI; +- 共享基础类型不导入业务模块;__main__ 只是薄命令入口。 + +依赖 W06/W07/W22/W24 的用例(宿主适配、协议、角色路由、旧库参数守卫、 +技能目录合同)在对应包落地后逐例实现,不以形状检查代替。 +""" + +from __future__ import annotations + +import ast +import json +import re +import subprocess +import sys +from pathlib import Path + +import muse + +仓库根 = Path(__file__).resolve().parents[2] +源码根 = 仓库根 / "src" / "muse" + + +def _遍历py(根: Path) -> list[Path]: + return sorted(p for p in 根.rglob("*.py") if "__pycache__" not in p.parts) + + +def _导入名(文件: Path) -> set[str]: + """AST 提取文件内全部 import 的顶层模块名。""" + 树 = ast.parse(文件.read_text(encoding="utf-8")) + 名单: set[str] = set() + for 节点 in ast.walk(树): + if isinstance(节点, ast.Import): + for 别名 in 节点.names: + 名单.add(别名.name.split(".")[0]) + elif isinstance(节点, ast.ImportFrom) and 节点.module and 节点.level == 0: + 名单.add(节点.module.split(".")[0]) + return 名单 + + +def test_skills_do_not_syspath_into_shared_implementations__f4e7a4() -> None: + """技能目录是方法与操作说明,不携带实现脚本,不注入 sys.path。""" + 技能根 = 仓库根 / ".agent" / "skills" + 脚本 = [p for p in 技能根.rglob("*.py") if "__pycache__" not in p.parts] + assert 脚本 == [], [str(p.relative_to(仓库根)) for p in 脚本] + for md in 技能根.rglob("*.md"): + 文本 = md.read_text(encoding="utf-8") + assert "sys.path" not in 文本, f"技能文档出现 sys.path 注入:{md.relative_to(仓库根)}" + + +def _字符串常量(文件: Path) -> list[str]: + """AST 提取全部字符串常量,排除模块/类/函数 docstring。""" + 树 = ast.parse(文件.read_text(encoding="utf-8")) + 文档: set[int] = set() + for 节点 in ast.walk(树): + if isinstance(节点, (ast.Module, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + 主体 = 节点.body + if 主体 and isinstance(主体[0], ast.Expr) and isinstance(主体[0].value, ast.Constant): + 文档.add(id(主体[0].value)) + return [ + 节点.value + for 节点 in ast.walk(树) + if isinstance(节点, ast.Constant) and isinstance(节点.value, str) and id(节点) not in 文档 + ] + + +def test_dashboard_does_not_import_skills__ba6ad5() -> None: + """src/mude 不以代码依赖 .agent 技能目录(文档中的合同引用不算依赖)。""" + for 文件 in _遍历py(源码根): + for 值 in _字符串常量(文件): + assert ".agent" not in 值, f"源码以路径方式引用 .agent:{文件.relative_to(仓库根)}" + assert "skills/" not in 值, f"源码引用技能路径:{文件.relative_to(仓库根)}" + + +def test_active_runtime_has_no_claude_cli_dependency__1ec1b5() -> None: + """活跃运行代码不依赖专有外部 CLI(claude/dsh 等);子进程只允许工程工具。""" + for 文件 in _遍历py(源码根): + 树 = ast.parse(文件.read_text(encoding="utf-8")) + for 节点 in ast.walk(树): + if isinstance(节点, ast.Call) and getattr(节点.func, "attr", "") == "run": + for 参数 in 节点.args: + if isinstance(参数, ast.List): + for 元素 in 参数.elts: + if isinstance(元素, ast.Constant) and isinstance(元素.value, str): + 值 = 元素.value + assert not (值.startswith("claude") or 值.startswith("dsh")), ( + f"依赖专有 CLI:{文件.relative_to(仓库根)} -> {值}" + ) + + +def test_runtime_does_not_import_muse_business__5d7edd() -> None: + """共享纯基础不反向导入业务;启动装配可以绑定具体实现。""" + 业务模块 = { + "作品规划", + "故事世界", + "资料研究", + "知识方法", + "正文写作", + "审校修订", + "作者经验", + "交付连载", + "上下文", + "效果评测", + } + 基础文件 = list((源码根 / "共享").glob("*.py")) + [ + 源码根 / "配置.py", + 源码根 / "资源加载.py", + ] + assert 基础文件, "共享与装配文件缺失" + for 文件 in 基础文件: + 树 = ast.parse(文件.read_text(encoding="utf-8")) + for 节点 in ast.walk(树): + if isinstance(节点, ast.ImportFrom) and 节点.module: + 顶层 = 节点.module.split(".")[0] == "muse" + if 顶层 and len(节点.module.split(".")) > 1: + 第二层 = 节点.module.split(".")[1] + assert 第二层 not in 业务模块, ( + f"基础层导入业务模块:{文件.relative_to(仓库根)} -> {节点.module}" + ) + + +def test_dispatch_agent_task_is_thin_cli__b141f1() -> None: + """python -m muse 是薄入口:只做参数解析与转交,无业务逻辑与连接。""" + 文件 = 源码根 / "__main__.py" + 文本 = 文件.read_text(encoding="utf-8") + 导入 = _导入名(文件) + assert 导入 <= {"__future__", "argparse", "sys", "pathlib", "muse"}, 导入 + for 禁止 in ("socket", "psycopg", "http", "urllib", "subprocess"): + assert 禁止 not in 文本, f"命令入口包含 {禁止}" + + +# --------------------------------------------------------------------------- +# 新增用例(W02 补充):安装与导入边界 +# --------------------------------------------------------------------------- + + +def test_import_from_any_cwd_without_external_connections__e0a1b2() -> None: + """任意 cwd 导入 muse 全部顶层模块且不产生任何 socket 连接(安装验收)。""" + 代码 = ( + "import socket\n" + "def _拒绝(self, 地址):\n" + " raise AssertionError(f'导入期间尝试连接 {地址}')\n" + "socket.socket.connect = _拒绝\n" + "import muse, muse.启动, muse.配置, muse.资源加载, muse.共享\n" + "print(muse.__version__)\n" + ) + 进程 = subprocess.run( + [sys.executable, "-c", 代码], cwd="/", capture_output=True, text=True, timeout=60 + ) + assert 进程.returncode == 0, 进程.stderr + assert 进程.stdout.strip() == muse.__version__ + + +def test_import_resolves_to_new_package_not_old_tree__c1d2e3() -> None: + """import muse 解析到 src/muse 安装包,不被旧树同名目录遮挡。""" + 代码 = "import muse\nprint(muse.__file__)\n" + 进程 = subprocess.run( + [sys.executable, "-c", 代码], + cwd=仓库根, + capture_output=True, + text=True, + timeout=60, + ) + assert 进程.returncode == 0, 进程.stderr + assert "src/muse" in 进程.stdout, 进程.stdout + + +# --------------------------------------------------------------------------- +# W13 承接:作者自然语言入口与角色路由合同(原 AuthorInstructionContractTest) +# --------------------------------------------------------------------------- + +作者根 = 仓库根 / ".agent" / "作者" +场景根 = 作者根 / "场景" +标准索引表头 = ( + "| 名称 | 相对地址 | 内容描述 | 使用场景 | 使用要求 |\n" + "|------|----------|----------|----------|----------|" +) +场景清单 = ("定故事", "排故事", "塑人物", "写下一章", "改正文", "查作品") +技能链接 = re.compile(r"\[([^\]\n]+)\]\(([^)\n]+/SKILL\.md)\)") +frontmatter名称 = re.compile(r"^name:\s*(.+?)\s*$", re.MULTILINE) +# 已有新版实现并登记进运行资源包的技能:场景路由必须指向新版路径,不再回到旧 muse 树。 +已替代技能 = { + "完善故事基础设定": ".agent/skills/操作/完善故事基础设定/SKILL.md", + "决定正文候选去留": ".agent/skills/操作/决定正文候选去留/SKILL.md", + "确认规划候选": ".agent/skills/操作/确认规划候选/SKILL.md", +} +退役技能别名 = { + "生成下一章", + "局部重写", + "场景扩写", + "正文润色", + "作品定盘", + "全书规划", + "章级细纲", + "全书解析", + "章后抽取", + "一致性检测", + "质量评分", +} + + +def _章节(文本: str, 标题: str) -> str: + 匹配 = re.search( + rf"^## {re.escape(标题)}\s*$\n(?P<正文>.*?)(?=^## |\Z)", + 文本, + flags=re.MULTILINE | re.DOTALL, + ) + assert 匹配 is not None, f"缺少章节:## {标题}" + return 匹配.group("正文") + + +def test_author_layer_has_discoverable_structure__41d4cb() -> None: + """作者层文件完整且各级索引可发现;路由不依赖技能自动发现。""" + 期望 = { + 作者根 / "目录.md", + 作者根 / "指令.md", + 作者根 / "原生中文写作.md", + 场景根 / "目录.md", + *(场景根 / f"{名称}.md" for 名称 in 场景清单), + } + 缺失 = sorted(str(p.relative_to(仓库根)) for p in 期望 if not p.is_file()) + assert 缺失 == [], "作者指令层文件不完整:\n" + "\n".join(缺失) + + 根索引 = (仓库根 / ".agent" / "目录.md").read_text(encoding="utf-8") + 项目入口 = (仓库根 / "AGENTS.md").read_text(encoding="utf-8") + 作者索引 = (作者根 / "目录.md").read_text(encoding="utf-8") + 场景索引 = (场景根 / "目录.md").read_text(encoding="utf-8") + assert "作者/目录.md" in 根索引 + assert ".agent/作者/指令.md" in 项目入口 + assert 作者索引.startswith(标准索引表头) + assert 场景索引.startswith(标准索引表头) + assert "[指令.md](指令.md)" in 作者索引 + assert "[原生中文写作.md](原生中文写作.md)" in 作者索引 + assert "[目录.md](场景/目录.md)" in 作者索引 + for 场景 in 场景清单: + assert f"[{场景}.md]({场景}.md)" in 场景索引 + + +def test_scenes_route_only_to_registered_chinese_skills__5e199d() -> None: + """场景路由只指向真实存在且 frontmatter 名称一致的技能;已替代技能不再路由旧实现。""" + 登记能力 = { + Path(条目).name + for 条目 in json.loads((仓库根 / "工具" / "资源登记.json").read_text(encoding="utf-8"))[ + "能力文件" + ] + } + for 场景 in 场景清单: + 源 = 场景根 / f"{场景}.md" + 路由章节 = _章节(源.read_text(encoding="utf-8"), "正式技能") + for 标签, 原目标 in 技能链接.findall(路由章节): + 目标 = (源.parent / 原目标).resolve() + assert 目标.is_file(), f"{源}: 技能链接不存在:{原目标}" + 匹配 = frontmatter名称.search(目标.read_text(encoding="utf-8")) + assert 匹配 is not None, f"{目标}: 缺少 frontmatter name" + assert 标签 == 匹配.group(1).strip(), f"{源}: 链接文字必须使用正式技能名" + 相对 = 目标.relative_to(仓库根).as_posix() + if 标签 in 已替代技能: + assert 相对 == 已替代技能[标签], ( + f"{源}: {标签} 已有新版实现,应路由 {已替代技能[标签]} 而不是 {相对}" + ) + if "muse/" not in 相对: + assert Path(相对).name in 登记能力, f"{源}: 未登记的新版技能:{相对}" + assert 标签 not in 退役技能别名 + # 作者层不是技能:不得出现作者入口冒充技能登记。 + assert 登记能力.isdisjoint({"目录.md"}) + + +def test_interaction_and_native_chinese_contracts_are_complete__7239c7() -> None: + """交互边界与原生中文写作合同的关键短语在作者层保持完整。""" + 入口 = (作者根 / "指令.md").read_text(encoding="utf-8") + for 必含 in ( + "一轮只问一个关键问题", + "二至三个中文选项", + "采用", + "修改", + "丢弃", + "Shadow", + "Canonical", + "只给正文", + "主会话不代替角色", + ): + assert 必含 in 入口, f"作者指令缺少交互合同短语:{必含}" + + 写作 = (作者根 / "原生中文写作.md").read_text(encoding="utf-8") + for 必含 in ( + "中文语序", + "具体名词、动作和关系优先", + "作者声音、叙述者声音和角色声音", + "不得凭空增加", + "润色不得偷改情节事实", + "扩写不得新开场景或改变落点", + "重写不得越过作者指定范围", + "精确诊断", + ): + assert 必含 in 写作, f"原生中文写作合同缺少:{必含}" + + 诊断场景 = (场景根 / "查作品.md").read_text(encoding="utf-8") + 修改场景 = (场景根 / "改正文.md").read_text(encoding="utf-8") + assert "只查不改" in 诊断场景 + assert "作者指定范围" in 修改场景 + + +def test_role_prompts_route_with_registered_skill_names__4bead8() -> None: + """五个角色文件与角色合同一致并登记进运行资源包;角色层不使用退役技能别名。""" + 登记 = json.loads((仓库根 / "工具" / "资源登记.json").read_text(encoding="utf-8")) + 能力 = set(登记["能力文件"]) + 角色合同 = { + "writer": "写手", + "planner": "规划员", + "extractor": "知识抽取员", + "detector": "检测员", + "judge": "评委", + } + for 角色ID, 名称 in 角色合同.items(): + 相对 = f".agent/角色/{名称}.md" + assert 相对 in 能力, f"角色未登记进运行资源包:{相对}" + 文本 = (仓库根 / 相对).read_text(encoding="utf-8") + 头部 = 文本.split("---")[1] + assert f"id: {角色ID}" in 头部 + assert f"name: {名称}" in 头部 + assert "contract_version:" in 头部 + for 别名 in 退役技能别名: + assert f"`{别名}`" not in 文本, f"{相对}: 角色文件引用退役技能别名 {别名}" diff --git a/tests/架构/test_测试依赖与执行证据.py b/tests/架构/test_测试依赖与执行证据.py new file mode 100644 index 0000000..83f436a --- /dev/null +++ b/tests/架构/test_测试依赖与执行证据.py @@ -0,0 +1,582 @@ +"""测试依赖与执行证据的逐例行为验证。 + +保护:用例选择、结构化报告、离线依赖阻断、超时与失败区分、 +空收集与缺环境不静默、登记范围与执行证据的拒绝语义。 + +环境:离线;受限子进程和临时测试项目(复制仓库真实 conftest 后在 +临时项目中运行 pytest,验证同一套隔离与守卫语义,不是旧文件迁移)。 +""" + +from __future__ import annotations + +import ast +import json +import shutil +import subprocess +import sys +import textwrap +import xml.etree.ElementTree as ET +from pathlib import Path + +import pytest + +仓库根 = Path(__file__).resolve().parents[2] +真实conftest = 仓库根 / "tests" / "conftest.py" + + +class 临时项目: + """受限临时 pytest 项目:复制真实 conftest,写入指定用例文件。""" + + def __init__(self, 根: Path) -> None: + self.根 = 根 + shutil.copyfile(真实conftest, 根 / "conftest.py") + + def 写(self, 相对: str, 内容: str) -> Path: + 目标 = self.根 / 相对 + 目标.parent.mkdir(parents=True, exist_ok=True) + 目标.write_text(textwrap.dedent(内容), encoding="utf-8") + return 目标 + + def 运行(self, *参数: str, 环境: dict[str, str] | None = None) -> tuple[int, dict]: + 报告 = self.根 / "报告.xml" + 完整 = [sys.executable, "-m", "pytest", "-q", f"--junitxml={报告}", *参数] + 清单 = self.根 / "用例清单.json" + if not 清单.exists(): + 目标 = [] + for 文件 in self.根.rglob("test_*.py"): + for 节点 in ast.walk(ast.parse(文件.read_text())): + if isinstance(节点, ast.FunctionDef) and 节点.name.startswith("test_"): + 后缀 = 节点.name.rsplit("__", 1)[-1] + 目标.append( + { + "case_id": f"TC-x-{后缀}000000", + "file": 文件.relative_to(self.根).as_posix(), + "symbol": 节点.name, + } + ) + 清单.write_text(json.dumps({"cases": [], "new_cases": 目标}, ensure_ascii=False)) + 完整.extend(["--用例清单", str(清单)]) + 进程 = subprocess.run( + 完整, + cwd=self.根, + capture_output=True, + text=True, + timeout=180, + env={**(_清理环境()), **(环境 or {})}, + ) + 结果: dict = {"退出码": 进程.returncode, "stdout": 进程.stdout, "stderr": 进程.stderr} + if 报告.exists(): + 结果["报告"] = 解析junit(报告) + return 进程.returncode, 结果 + + +def _清理环境() -> dict[str, str]: + """子进程环境:剔除数据库变量,避免宿主环境泄漏进隔离语义。""" + import os + + 环境 = dict(os.environ) + 环境.pop("MUSE_TEST_DATABASE_URL", None) + return 环境 + + +def 解析junit(路径: Path) -> dict: + 树 = ET.parse(路径) + 根 = 树.getroot() + 用例 = [] + for 节点 in 根.iter("testcase"): + 条目: dict = {"名称": 节点.get("name", ""), "类": 节点.get("classname", "")} + for 子 in 节点: + if 子.tag not in {"failure", "error", "skipped"}: + continue + 条目["结果"] = 子.tag + 条目["消息"] = (子.get("message") or "")[:300] + if 子.text: + 条目["正文"] = 子.text[:300] + 用例.append(条目) + 汇总 = 根.find(".//testsuite") + return { + "用例": 用例, + "数量": int(汇总.get("tests", "0")) if 汇总 is not None else len(用例), + "失败": int(汇总.get("failures", "0")) if 汇总 is not None else 0, + "跳过": int(汇总.get("skipped", "0")) if 汇总 is not None else 0, + } + + +@pytest.fixture +def 项目(tmp_path: Path) -> 临时项目: + return 临时项目(tmp_path) + + +# --------------------------------------------------------------------------- +# 用例选择与结构化报告 +# --------------------------------------------------------------------------- + + +def test_selector_alias_and_intersection_are_reported_as_json__a5c7d4(项目: 临时项目) -> None: + """--case 与标记取交集只选中目标用例;结构化报告精确到单个用例。""" + 项目.写( + "test_样本.py", + """ + def test_目标行为__aa0001(): + assert 1 + 1 == 2 + + def test_另一行为__bb0002(): + assert 2 + 2 == 4 + """, + ) + 退出码, 结果 = 项目.运行("--case", "TC-x-aa0001000000") + assert 退出码 == 0, 结果["stderr"] + 报告 = 结果["报告"] + assert 报告["数量"] == 1, 报告 + assert 报告["用例"][0]["名称"] == "test_目标行为__aa0001" + assert "结果" not in 报告["用例"][0] # 通过:无 failure/skipped 节点 + + +def test_offline_dependency_is_blocked_without_running_child__09d22d(项目: 临时项目) -> None: + """数据库标记用例在缺环境时被阻断:子进程不执行、全跳过不算通过。""" + 项目.写( + "test_数据库用例.py", + """ + from pathlib import Path + + import pytest + + @pytest.mark.数据库 + def test_需要数据库__cc0003(隔离数据库URL): + Path("child_ran").write_text("ran", encoding="utf-8") + assert 隔离数据库URL + """, + ) + 退出码, 结果 = 项目.运行("--case", "TC-x-cc0003000000") + # 全跳过 → 守卫改写为退出码 5,不计通过;跳过原因可见 + assert 退出码 == 5, (退出码, 结果["stderr"]) + 报告 = 结果["报告"] + assert 报告["用例"][0]["结果"] == "skipped" + assert "MUSE_TEST_DATABASE_URL" in 报告["用例"][0]["消息"] + # 子进程没有执行:没有任何副作用文件 + assert not (项目.根 / "child_ran").exists() + + +def test_allow_requires_runs_and_still_reports_dependency__08eb1b(项目: 临时项目) -> None: + """提供隔离库引用后数据库用例真实执行;依赖来源在结果中可见。""" + 项目.写( + "test_数据库用例.py", + """ + import pytest + + @pytest.mark.数据库 + def test_获得隔离引用__cc0003(隔离数据库URL): + assert 隔离数据库URL == "postgresql://synthetic-test" + """, + ) + 退出码, 结果 = 项目.运行( + "--case", + "TC-x-cc0003000000", + 环境={"MUSE_TEST_DATABASE_URL": "postgresql://synthetic-test"}, + ) + assert 退出码 == 0, (退出码, 结果["stderr"]) + assert 结果["报告"]["数量"] == 1 + + +def test_nonzero_and_timeout_have_distinct_structured_statuses__306973(项目: 临时项目) -> None: + """断言失败与超时都以非零退出,且失败消息可区分。""" + 项目.写( + "test_失败.py", + """ + def test_断言失败__dd0004(): + assert 1 == 2, "合成失败" + """, + ) + 项目.写( + "test_睡眠.py", + """ + import time + + def test_超时睡眠__ee0005(): + time.sleep(30) + """, + ) + 码1, 结果1 = 项目.运行("--case", "TC-x-dd0004000000") + assert 码1 == 1 + 断言消息 = 结果1["报告"]["用例"][0]["消息"] + assert "合成失败" in 断言消息 + + 码2, 结果2 = 项目.运行("--case", "TC-x-ee0005000000", "--timeout=2") + assert 码2 == 1 + 超时正文 = 结果2["报告"]["用例"][0].get("正文", "") + 结果2["报告"]["用例"][0].get("消息", "") + assert "Timeout" in 超时正文 or "timeout" in 超时正文 + # 两者消息可区分:超时消息不含合成失败标记 + assert "合成失败" not in 超时正文 + + +def test_no_matches_and_missing_path_are_not_silent__ae03b5(项目: 临时项目) -> None: + """未知 case 选择与非空选择的空收集都以非零退出并说明原因。""" + 项目.写( + "test_存在.py", + """ + def test_存在用例__ff0006(): + assert True + """, + ) + 码1, 结果1 = 项目.运行("--case", "TC-x-0000zz9999") + assert 码1 != 0 + assert "--case" in 结果1["stderr"] or "--case" in 结果1["stdout"] + + 码2, 结果2 = 项目.运行("tests/不存在的路径") + assert 码2 in (4, 5) # 缺失路径或空收集:非零且不静默 + assert "no tests" in (结果2["stdout"] + 结果2["stderr"]).lower() + + +def test_invalid_manifest_and_selector_requirement_are_structured__f7150b(项目: 临时项目) -> None: + """非法清单与未登记选择分别失败,错误原因可区分。""" + 项目.写("test_任意.py", "def test_任意用例__000007():\n assert True\n") + 清单 = 项目.根 / "用例清单.json" + 清单.write_text("{broken", encoding="utf-8") + 码, 结果 = 项目.运行("--case", "TC-x-000007000000") + assert 码 == 4 + assert "用例清单" in 结果["stderr"] + 清单.write_text('{"cases": [], "new_cases": []}', encoding="utf-8") + 码, 结果 = 项目.运行("--case", "TC-x-000007000000") + assert 码 == 4 + assert "未登记" in 结果["stderr"] + + +def test_all_offline_is_explicit_opt_in_and_output_is_summary_only__265060(项目: 临时项目) -> None: + """默认运行按标记剔除外部环境用例;报告只含离线用例。""" + 项目.写( + "test_离线.py", + """ + def test_离线用例__110008(): + assert True + """, + ) + 项目.写( + "test_外部.py", + """ + import pytest + + @pytest.mark.数据库 + def test_外部用例__220009(隔离数据库URL): + assert 隔离数据库URL + """, + ) + 码, 结果 = 项目.运行("-m", "not 数据库") + assert 码 == 0, (码, 结果["stderr"]) + 名称 = [u["名称"] for u in 结果["报告"]["用例"]] + assert 名称 == ["test_离线用例__110008"], 名称 + + +def test_zero_exit_without_execution_evidence_fails_closed__50f80b(项目: 临时项目) -> None: + """空用例文件与纯跳过都不允许零退出冒充通过。""" + 项目.写( + "test_空.py", + """ + 说明 = "本文件没有任何测试函数" + """, + ) + 项目.写( + "test_跳过.py", + """ + import pytest + + def test_跳过用例__33000a(): + pytest.skip("合成跳过") + """, + ) + # 只选空文件:无收集 → 退出码 5 + 码1, 结果1 = 项目.运行("test_空.py") + assert 码1 == 5 + # 纯跳过:守卫改写退出码为 5,不计通过 + 码2, 结果2 = 项目.运行("test_跳过.py") + assert 码2 == 5, (码2, 结果2["stdout"]) + assert "零实际执行" in 结果2["stderr"] + + +# --------------------------------------------------------------------------- +# 登记范围(生成范围)检查:工具/维护索引.py 的同一语义 +# --------------------------------------------------------------------------- + +sys.path.insert(0, str(仓库根 / "工具")) +from 维护索引 import 检查用例身份 # noqa: E402 + + +def 写清单(根: Path, 目标: list[dict]) -> Path: + 清单 = 根 / "清单.json" + 清单.write_text( + json.dumps({"cases": [{"target_cases": 目标}], "new_cases": []}, ensure_ascii=False), + encoding="utf-8", + ) + return 清单 + + +def test_独立用例身份可绑定原生参数行__e2000b(tmp_path: Path) -> None: + 项目 = 临时项目(tmp_path) + 项目.写( + "test_参数.py", + """ + import pytest + @pytest.mark.parametrize("输入", [1, 2], ids=["first", "second"]) + def test_参数行为__abcdef(输入): + assert 输入 in (1, 2) + """, + ) + 项目.写( + "用例清单.json", + json.dumps( + { + "cases": [], + "new_cases": [ + { + "case_id": "NC-first", + "file": "test_参数.py", + "symbol": "test_参数行为__abcdef", + "parameter_ids": ["first"], + }, + { + "case_id": "NC-second", + "file": "test_参数.py", + "symbol": "test_参数行为__abcdef", + "parameter_ids": ["second"], + }, + ], + } + ), + ) + 退出, 结果 = 项目.运行("--case", "NC-first") + assert 退出 == 0, 结果 + 树 = ET.parse(tmp_path / "报告.xml") + 行 = 树.findall(".//testcase") + assert len(行) == 1 and 行[0].attrib["name"].endswith("[first]") + assert 行[0].find(".//property[@name='case_id']").attrib["value"] == "NC-first" + + +def test_generated_scope_rejects_unregistered_disk_asset__69440b(tmp_path: Path) -> None: + """磁盘上存在但清单未登记的用例被拒绝(未登记资产)。""" + 测试文件 = tmp_path / "tests" / "registered" / "test_登记.py" + 测试文件.parent.mkdir(parents=True) + 测试文件.write_text("def test_已登记__44000b():\n assert True\n", encoding="utf-8") + 未登记 = tmp_path / "tests" / "new" / "test_未登记.py" + 未登记.parent.mkdir(parents=True) + 未登记.write_text("def test_未登记__55000c():\n assert True\n", encoding="utf-8") + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-44000b000000", + "file": "tests/registered/test_登记.py", + "symbol": "test_已登记__44000b", + } + ], + ) + 问题 = 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) + assert any("test_未登记__55000c" in p and "未在用例清单登记" in p for p in 问题), 问题 + + +def test_generated_scope_uses_manifest_scan_roots__20f4fa(tmp_path: Path) -> None: + """自定义扫描根下的已登记用例被接受(无问题)。""" + 测试文件 = tmp_path / "custom-tests" / "test_自定义.py" + 测试文件.parent.mkdir(parents=True) + 测试文件.write_text("def test_自定义用例__66000d():\n assert True\n", encoding="utf-8") + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-66000d000000", + "file": "custom-tests/test_自定义.py", + "symbol": "test_自定义用例__66000d", + } + ], + ) + assert 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) == [] + + +def test_generated_scope_rejects_manifest_extra_asset__b4901f(tmp_path: Path) -> None: + """清单登记的目标在磁盘上不存在时被拒绝。""" + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-77000e000000", + "file": "tests/removed/test_已删除.py", + "symbol": "test_已删除__77000e", + } + ], + ) + 问题 = 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) + assert any("目标不存在" in p for p in 问题), 问题 + + +def test_generated_scope_accepts_existing_behavior_eval_entry__5b8bb2(tmp_path: Path) -> None: + """已存在的真实模型评测用例登记被接受(含环境标记)。""" + 测试文件 = tmp_path / "tests" / "evals" / "test_评测.py" + 测试文件.parent.mkdir(parents=True) + 测试文件.write_text( + "import pytest\n\n@pytest.mark.真实模型\ndef test_评测用例__88000f():\n assert True\n", + encoding="utf-8", + ) + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-88000f000000", + "file": "tests/evals/test_评测.py", + "symbol": "test_评测用例__88000f", + } + ], + ) + assert 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) == [] + + +def test_generated_scope_rejects_missing_behavior_eval_entry__fe3fba(tmp_path: Path) -> None: + """登记的真实模型评测用例文件缺失时被拒绝。""" + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-990010000000", + "file": "tests/evals/test_缺失评测.py", + "symbol": "test_缺失评测__990010", + } + ], + ) + 问题 = 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) + assert any("目标不存在" in p for p in 问题), 问题 + + +def test_generated_scope_ignores_non_test_helpers_under_test_roots__14f941(tmp_path: Path) -> None: + """测试根下的非 test_ 辅助函数不触发登记检查。""" + 测试文件 = tmp_path / "tests" / "helpers" / "test_辅助.py" + 测试文件.parent.mkdir(parents=True) + 测试文件.write_text( + "def 辅助函数():\n" + " return 1\n" + "\n" + "\n" + "def test_真用例__a10011():\n" + " assert 辅助函数() == 1\n", + encoding="utf-8", + ) + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-a10011000000", + "file": "tests/helpers/test_辅助.py", + "symbol": "test_真用例__a10011", + } + ], + ) + assert 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) == [] + + +def test_generated_scope_blocks_empty_and_print_only_scripts_before_child__36f79e( + tmp_path: Path, +) -> None: + """无可判定语句的 print 型用例在执行前即被登记检查拒绝。""" + 空文件 = tmp_path / "tests" / "empty" / "test_空断言.py" + 空文件.parent.mkdir(parents=True) + 空文件.write_text("def test_空用例__b20012():\n print('只有输出')\n", encoding="utf-8") + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-b20012000000", + "file": "tests/empty/test_空断言.py", + "symbol": "test_空用例__b20012", + } + ], + ) + 问题 = 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) + assert any("无可判定语句" in p for p in 问题), 问题 + + +def test_generated_scope_accepts_supported_python_shapes__7c1382(tmp_path: Path) -> None: + """函数式与类式用例形状都被识别并核对。""" + 测试文件 = tmp_path / "tests" / "shapes" / "test_形状.py" + 测试文件.parent.mkdir(parents=True) + 测试文件.write_text( + "class Test类式:\n" + " def test_类方法__c30013(self):\n" + " assert True\n" + "\n" + "\n" + "def test_函数式__d30014():\n" + " assert True\n", + encoding="utf-8", + ) + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "TC-x-c30013000000", + "file": "tests/shapes/test_形状.py", + "symbol": "test_类方法__c30013", + }, + { + "case_id": "TC-x-d30014000000", + "file": "tests/shapes/test_形状.py", + "symbol": "test_函数式__d30014", + }, + ], + ) + assert 检查用例身份(tmp_path, 清单, 核对缺失目标=True, 全树扫描=True) == [] + + +def test_完整身份不接受冒用后缀__a02001(项目: 临时项目) -> None: + """未登记 ID 与真实符号后缀相同,也不能选择执行。""" + 项目.写("test_样本.py", "def test_目标__aa0001():\n assert True\n") + 码, 结果 = 项目.运行("--case", "XX-forged-aa0001000000") + assert 码 == 4 + assert "未登记" in 结果["stderr"] + + +def test_完整身份绑定文件与符号__a02002(tmp_path: Path) -> None: + """另一文件冒用已登记后缀不能冒充目标用例。""" + (tmp_path / "test_冒用.py").write_text("def test_冒用__aa0001():\n assert True\n") + 清单 = 写清单( + tmp_path, + [{"case_id": "TC-x-aa0001000000", "file": "test_原例.py", "symbol": "test_原例__aa0001"}], + ) + 问题 = 检查用例身份(tmp_path, 清单, 全树扫描=True) + assert any("未在用例清单登记" in 条 for 条 in 问题), 问题 + + +@pytest.mark.数据库 +@pytest.mark.parametrize("阶段", ["收集", "测试体", "子进程"], ids=["collection", "call", "child"]) +def test_离线进程拒绝真实数据库连接__a02005(项目: 临时项目, 隔离数据库URL: str, 阶段: str) -> None: + """先确认隔离库可达,再由未标记的子测试验证 OS 阻断 libpq。""" + import psycopg + + with psycopg.connect(隔离数据库URL) as 连: + assert 连.execute("SELECT 1").fetchone() == (1,) + 连接代码 = "import os, psycopg; psycopg.connect(os.environ['MUSE_TEST_DATABASE_URL'])" + if 阶段 == "收集": + 内容 = 连接代码 + "\ndef test_试连接__aa0001():\n assert True\n" + elif 阶段 == "子进程": + 内容 = ( + "import subprocess, sys\n" + "def test_试连接__aa0001():\n" + f" subprocess.run([sys.executable, '-c', {连接代码!r}], check=True)\n" + ) + else: + 内容 = "def test_试连接__aa0001():\n " + 连接代码 + "\n" + 项目.写("test_连接.py", 内容) + 码, 结果 = 项目.运行(环境={"MUSE_TEST_DATABASE_URL": 隔离数据库URL}) + assert 码 in (1, 2) + assert "Operation not permitted" in 结果["stdout"] + 结果["stderr"] + + +def test_已执行目标丢失不被过渡期忽略__a02006(tmp_path: Path) -> None: + 清单 = 写清单( + tmp_path, + [ + { + "case_id": "NC-existing-test", + "file": "tests/架构/test_已执行.py", + "symbol": "test_已执行__aa0001", + "verification_state": "通过", + } + ], + ) + 问题 = 检查用例身份(tmp_path, 清单) + assert any("目标不存在" in x for x in 问题), 问题 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..d13a919 --- /dev/null +++ b/uv.lock @@ -0,0 +1,682 @@ +version = 1 +revision = 2 +requires-python = ">=3.12" + +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, +] + +[[package]] +name = "anyio" +version = "4.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "certifi" +version = "2026.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "click" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "fastapi" +version = "0.141.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/02/91e3416a8fdd715abb903a952a6bec7cdd8d14eed55d415fc8595524c319/fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1", size = 425799, upload-time = "2026-07-29T17:18:05.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "muse" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "fastapi" }, + { name = "httpx" }, + { name = "itsdangerous" }, + { name = "jsonschema" }, + { name = "psycopg", extra = ["binary"] }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "uvicorn" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-timeout" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", specifier = ">=0.115" }, + { name = "httpx", specifier = ">=0.28" }, + { name = "itsdangerous", specifier = ">=2.2" }, + { name = "jsonschema", specifier = ">=4.23" }, + { name = "psycopg", extras = ["binary"], specifier = ">=3.2" }, + { name = "pydantic", specifier = ">=2.0" }, + { name = "pyyaml", specifier = ">=6.0" }, + { name = "uvicorn", specifier = ">=0.30" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pyright", specifier = ">=1.1.390" }, + { name = "pytest", specifier = ">=8.3" }, + { name = "pytest-timeout", specifier = ">=2.3" }, + { name = "ruff", specifier = ">=0.8" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "psycopg" +version = "3.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/73/8fb739d0f6bba247b9b93c9840c402a4f88545be5f1d4b02b23366371c00/psycopg-3.3.5.tar.gz", hash = "sha256:d0a3d9ccf5788af054cbd745278cb02401b5c312aeaafbf2c6144460aec47da4", size = 166508, upload-time = "2026-08-31T22:45:43.151Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/2e/d0a645bcaadde68bd6d93c43f02f14b0191bdda367ce3f7722abe3da744a/psycopg-3.3.5-py3-none-any.whl", hash = "sha256:ce5aa5cdb4f9379f00f487590e5890bfa7df9a164648c969ffa628505e21af4e", size = 213598, upload-time = "2026-08-31T22:39:02.184Z" }, +] + +[package.optional-dependencies] +binary = [ + { name = "psycopg-binary", marker = "implementation_name != 'pypy'" }, +] + +[[package]] +name = "psycopg-binary" +version = "3.3.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/83/ba396428a4fb6b70f0dd41315ad86a2c14441b7214afd47b2d49cb450b78/psycopg_binary-3.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:25105f9b46bdf2a30fcb67f56976ed66f6855941ae16bc024192609b917d493c", size = 4700169, upload-time = "2026-08-31T22:41:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/4f/56/b23c5978e55cf4effdc5a3e13a17d69a580a487993e01b7c3768dd24281c/psycopg_binary-3.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0249c3e960cdee686000eb77169fb6590105c05bacc37e057ccdffdcd8e6ebde", size = 4763037, upload-time = "2026-08-31T22:41:47.571Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d8/b41108bfe194b4098076c8b873b05ec2ca9582445eccfd9075970d7b948e/psycopg_binary-3.3.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5698ab5941a4d138c30fef858588e651fe7d583280cd6e41832825ad9e747750", size = 5546232, upload-time = "2026-08-31T22:41:55.817Z" }, + { url = "https://files.pythonhosted.org/packages/21/d1/0f244dfef389e52e9dc3056f2a9033d1f6901e97d24d9a9c8b836e32ab6b/psycopg_binary-3.3.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:682a17a57415c3ca1731eec018ed031f012ffcb81ba74806eb219cb396065672", size = 5227752, upload-time = "2026-08-31T22:42:03.776Z" }, + { url = "https://files.pythonhosted.org/packages/31/88/a4781365f09807fb91435e2d00096f3f6ae5d06bce15ef3c3c385dc22772/psycopg_binary-3.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2a61e8147902771df7efe14062a3c8736347850d0d8befcf048235752504f2e", size = 6824658, upload-time = "2026-08-31T22:42:13.263Z" }, + { url = "https://files.pythonhosted.org/packages/65/f1/072c4a46287644694731b3e40fab120ebacf6d153cce7ebf7a5b208f5561/psycopg_binary-3.3.5-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f7e1e45aad410e20de45df2b159df68ff6c8dbf47a3501f806c4489b27f4ad2b", size = 5061439, upload-time = "2026-08-31T22:42:18.591Z" }, + { url = "https://files.pythonhosted.org/packages/e2/d6/1776a95c16941b8bbce89407cc7cf9ea3fd557efb503a40873c9e2b6394f/psycopg_binary-3.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c09775c549b40b274206e1b043c5e5b5af39666e85c98382a30bd05d23ab677b", size = 4588586, upload-time = "2026-08-31T22:42:25.23Z" }, + { url = "https://files.pythonhosted.org/packages/82/64/44ec87b9a74faebe966856307b65c0d35ee6321ce509cdd0e8d6a3f5338b/psycopg_binary-3.3.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:cf0e5e63ee86098299c673992053d556c489ba9ae6aca6cb6e24d16a8e0b09e6", size = 4265161, upload-time = "2026-08-31T22:42:31.864Z" }, + { url = "https://files.pythonhosted.org/packages/24/88/cf181df5651395a80afc520f5fefac72beb035c0c9d58ab7fcc880c9b221/psycopg_binary-3.3.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c065531e8c1815276f50dbfa283e3a7f022671414cdda6fa9a16794dd53b28f9", size = 3998727, upload-time = "2026-08-31T22:42:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/43/1e/c72a1107647db4bb3f534c7fe1b57b1957d9c099e795f5aa81f4a2e0c312/psycopg_binary-3.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:df9853b832b7b916e02ef68e0d5403a7dab2d5c1ddfe94f22b1b155eb862622f", size = 4310119, upload-time = "2026-08-31T22:42:46.403Z" }, + { url = "https://files.pythonhosted.org/packages/03/8d/452620608cafff164737e20b42ebffee8151b865ee171ba0d6692a560a44/psycopg_binary-3.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:35885e333020fc152d27bea1a494bef13b2e68f6fd92b6229015e93539152008", size = 3648197, upload-time = "2026-08-31T22:42:51.575Z" }, + { url = "https://files.pythonhosted.org/packages/e0/1c/e718752cc63cf4e99e4a10fd36e3a3364dabdd0819484a24c0d79fbb9685/psycopg_binary-3.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6e85d50b87257fb117675a19ee59daa7bf9a57f6431500adf7059df799232ef4", size = 4704421, upload-time = "2026-08-31T22:42:59.564Z" }, + { url = "https://files.pythonhosted.org/packages/af/cf/a0e748e27c09b92738e4460582d121ba1908be3e36791e150f435e54b332/psycopg_binary-3.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e5becd311f9af8d180bad372f51fb2252fd02cb2073056e2b170c9274f95fe7f", size = 4765054, upload-time = "2026-08-31T22:43:05.421Z" }, + { url = "https://files.pythonhosted.org/packages/39/62/0cbac0266d56c94dd1f702d9af2b5d54bf80b6e658048f4f2c5bd63dd7e3/psycopg_binary-3.3.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:19e5bf9872dbd164c220567fd385ba2309c7d9df1541f78343510c6b0f36a1b7", size = 5547137, upload-time = "2026-08-31T22:43:11.768Z" }, + { url = "https://files.pythonhosted.org/packages/fb/3a/73c6f8871f38fc07a9c0b4cbc9467beb116a2783cecf87dfa53900a396dc/psycopg_binary-3.3.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb3b3bffebfe07110730626e76238161124f35ac87b748d663316a28d22f58b0", size = 5227577, upload-time = "2026-08-31T22:43:20.767Z" }, + { url = "https://files.pythonhosted.org/packages/59/7b/9f17b9f4d297b774dc574199c4dcd02dadc32a00c4056265918de5c70635/psycopg_binary-3.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2111f880add40fb03c60556069ad68e884a0908a74d2debafc603caf93b73552", size = 6824606, upload-time = "2026-08-31T22:43:33.698Z" }, + { url = "https://files.pythonhosted.org/packages/94/86/d84dadd94a004dbbb43ce0579f1f766fdc6b8cbef745090e24bea77b5283/psycopg_binary-3.3.5-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:40505676b1526b9ea387dace034040a8c8b0bcf984cd6bd4720a2ab15e813586", size = 5060854, upload-time = "2026-08-31T22:43:42.258Z" }, + { url = "https://files.pythonhosted.org/packages/30/d0/e5078be2c7d2490c0d6cd4cb7b3601aec44be2fa8b3fe927e9419b06004f/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5816472e3bb05615f33a741e0835043d1f4bf9709ff30d2f4aed71815cfc6b5e", size = 4589511, upload-time = "2026-08-31T22:43:50.012Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/08dfb5b18fa482e025864fd91a022310d0782c42e4cf5dda5d0e010b6790/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:358748fc4c8ccdc0e2bdf55420494930e19c3ade586ea9c3a6de3dad1f897311", size = 4268144, upload-time = "2026-08-31T22:43:56.993Z" }, + { url = "https://files.pythonhosted.org/packages/88/b9/cb01dc1d63f3241b49b2ca7fb9f98d0f5c76127f0dbb9440635a6ad0233e/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1ef2e498be47800f6202b9a2304c22646325ca6d54001b7c785bcfdb24a1e8ab", size = 4001036, upload-time = "2026-08-31T22:44:04.429Z" }, + { url = "https://files.pythonhosted.org/packages/95/49/7c17dd832c05b380562ff2ff5f6ab2bcaeca8b7fff2c2b355854368a5bdb/psycopg_binary-3.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:88e01aa2e938a45655a8a5213fc3a44ba78cb4cab8a569b3e0bcb3d1d0eaba16", size = 4313112, upload-time = "2026-08-31T22:44:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2c/b0b2f887185d6a2ec0b3bef948cc07656d2d1a5d96fa7f2bb03f6ef06ca4/psycopg_binary-3.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:ba466011569297114449df9d523438e1adeedf3e4f31ffb78e897ec3fef3076b", size = 3647313, upload-time = "2026-08-31T22:44:19.139Z" }, + { url = "https://files.pythonhosted.org/packages/46/7f/4e2395da194558533bd9c31f35e4dc58ecbbae6a7176b0d2f72d629e8a51/psycopg_binary-3.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:40f8b132c7243ef5f503f0b6f986bf16d38a51b0df1c6ba2577743f128be03e3", size = 4712612, upload-time = "2026-08-31T22:44:25.723Z" }, + { url = "https://files.pythonhosted.org/packages/ae/94/fdb2093c8ccd7048449156db526ad746740cf34fe9c01e5dc1b7a7a8b257/psycopg_binary-3.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c0cac998b9b1e82dec853d2e53b3d34d56a525cf231f9441a636cfd5992929a9", size = 4775139, upload-time = "2026-08-31T22:44:36.719Z" }, + { url = "https://files.pythonhosted.org/packages/31/52/5195e87960715f7be2005761b72d56fce4e7757d5333fd40384f071c2de1/psycopg_binary-3.3.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:479b96fd78149cfa10369dc53fbfb89ee729be13146b584a23dbc7e164c0cf1e", size = 5556807, upload-time = "2026-08-31T22:44:42.668Z" }, + { url = "https://files.pythonhosted.org/packages/f7/42/2d616210a91e1327516ed5ae71961aaa31bb740e4a61d7190f2221685a40/psycopg_binary-3.3.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f45d77e398542ce0937d9fa3cd9d84e9c5fc6b34c50a66404ae840bada312750", size = 5236206, upload-time = "2026-08-31T22:44:47.943Z" }, + { url = "https://files.pythonhosted.org/packages/08/2e/e54b0d4cc263b3526e3728bb50a69660d5e79e52255ccd2a1a71e40e6f9a/psycopg_binary-3.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98a388509306e5e08a4203253ac52846bc1b034e5cbd0ae6da1211593cc28594", size = 6838066, upload-time = "2026-08-31T22:44:55.701Z" }, + { url = "https://files.pythonhosted.org/packages/77/80/ec22a110f81a44c411965982097efeee86006bdd5a1f51f628318106c84c/psycopg_binary-3.3.5-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ab39e2794b95af61a2ff69e33e5ab6ac5df36e9ffea9a3b18e38b2aaca8c5ad5", size = 5072036, upload-time = "2026-08-31T22:45:02.838Z" }, + { url = "https://files.pythonhosted.org/packages/93/55/7bc3c3ac769ab4fe0c619f2179b4aab3ae043f4d478283dd8279d24c0be4/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9c071bf78e5c2e6efa40bc9089a954d7b41221347a72f35c6bf2d8c96e632f75", size = 4604058, upload-time = "2026-08-31T22:45:10.444Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/8e7414f7dc10b2959e664330cdcf393e412f67355975dafa876cef265264/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:14fdfd65a96ecbd8b586d14546105641f4a6ac7cbe335c786830ea4de94bbe60", size = 4284766, upload-time = "2026-08-31T22:45:17.881Z" }, + { url = "https://files.pythonhosted.org/packages/c1/72/33f293c1d3ee9114f47e9ef4880f31c9de864e84a9b09454c26e106c25ed/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:8dbd694f3741dd4ac5bc60b70e17f7841aefb3f0f38cef4d2756de270e03af43", size = 4011958, upload-time = "2026-08-31T22:45:24.792Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c9/8e38840e5a7d006987bbc9acb29951912253fa50549a4db92b3aa535f089/psycopg_binary-3.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:14f432430fd9e1a9e7d9ab2fe14956c77f5d074ebdc556a1ad04e9a1bd3fca04", size = 4323273, upload-time = "2026-08-31T22:45:32.973Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c7/b7ebf601c307f93e7c4c4ebac0edc9db3b2729ca038efe700a18f86b5517/psycopg_binary-3.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:df209e64674a34b41662c67fdc8b4e0ffd77d2136393790691d086a09f9a6cab", size = 3745885, upload-time = "2026-08-31T22:45:40.537Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", size = 845750, upload-time = "2026-08-28T14:04:00.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", size = 472589, upload-time = "2026-08-28T14:03:59.136Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", size = 472262, upload-time = "2026-08-28T10:01:31.677Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3f/76358795aa7a8c6d4f36e2cb828ad1c90ee118e1393a9281664f5aade9d4/pydantic_core-2.46.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d", size = 2076516, upload-time = "2026-08-28T09:58:21.576Z" }, + { url = "https://files.pythonhosted.org/packages/db/50/26b091836076ce4cb2fac264186936acc069e0595772cfd02a563bc4761a/pydantic_core-2.46.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e", size = 1922874, upload-time = "2026-08-28T09:58:23.766Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/2a8ce3849e299d44e2d2c196b6082643a3235565a735cb51db7a6261f614/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29", size = 1951772, upload-time = "2026-08-28T09:58:25.435Z" }, + { url = "https://files.pythonhosted.org/packages/87/46/ac0dc8bdd9e6048183a14eb127764e7ad9240021c17513074a4711b0e31e/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4", size = 2031832, upload-time = "2026-08-28T09:58:27.102Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/339de5bef7be36301a2231eaa52e62163742c2281f11b5f4892bc79785cd/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a", size = 2208645, upload-time = "2026-08-28T09:58:28.948Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a0/9ff22b797724262da14427abaed4dd1d864a139693fc5e7809114376a716/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62", size = 2265935, upload-time = "2026-08-28T09:58:30.625Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a4/eb9409ec0736e50aa70a412f16c204ed149516846912f7e6724d4c73ee53/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2", size = 2066284, upload-time = "2026-08-28T09:58:32.289Z" }, + { url = "https://files.pythonhosted.org/packages/c0/02/7f6156ffc926857f1c37c07d9a388682865a81830ab6a1b637082c25e399/pydantic_core-2.46.5-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869", size = 2105889, upload-time = "2026-08-28T09:58:33.986Z" }, + { url = "https://files.pythonhosted.org/packages/92/b1/e781d357ebe09fc929f995700f1b3503e8897f1cece183ecb1300d4d67e9/pydantic_core-2.46.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5", size = 2158006, upload-time = "2026-08-28T09:58:35.647Z" }, + { url = "https://files.pythonhosted.org/packages/70/0a/644597d84ab400e50609c192120b85c9681c22d3a20461b9060a79be0a7a/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3", size = 2158408, upload-time = "2026-08-28T09:58:37.38Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ee/ca3b7b3a4b3769ffe9ce9432a7c9be755de9593a46d3b0d54d0409323e44/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b", size = 2309609, upload-time = "2026-08-28T09:58:39.22Z" }, + { url = "https://files.pythonhosted.org/packages/ce/52/39fa1f451486019524ca685020390e7ca351832fd874530ba30c8628e6dc/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0", size = 2342618, upload-time = "2026-08-28T09:58:40.89Z" }, + { url = "https://files.pythonhosted.org/packages/81/5e/468fc630568c61dcef3cd47ad32ffbeed9af643f49208d1ea86ab4f890c4/pydantic_core-2.46.5-cp312-cp312-win32.whl", hash = "sha256:5cb482e9e84c851f4e623fe4acc1ced89168cf1fe18f7089db4548c8f5bbb65b", size = 1939475, upload-time = "2026-08-28T09:58:42.591Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c9/4c19f41b84cf6b622a72fbeed7665b25d47a187d68d47d0d430c07f23268/pydantic_core-2.46.5-cp312-cp312-win_amd64.whl", hash = "sha256:5e81740c09e310f5aa5cbd3e434a01c154d4bef93241c7877b39f211d2b78ba8", size = 2043140, upload-time = "2026-08-28T09:58:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/af/dd/0c1a050299147c746e5256db16d645ab5efd4f78c59937d581a0524e74a2/pydantic_core-2.46.5-cp312-cp312-win_arm64.whl", hash = "sha256:f7b0ec93a2893de856652154d73b7ba622f26fa97726487dcac373de5f4c6084", size = 1997729, upload-time = "2026-08-28T09:58:46.13Z" }, + { url = "https://files.pythonhosted.org/packages/f5/37/5abe39a8372a61d3dc3c1338fc504281c01b32fdb3169cd7187153b56d3e/pydantic_core-2.46.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0", size = 2075885, upload-time = "2026-08-28T09:58:47.856Z" }, + { url = "https://files.pythonhosted.org/packages/21/43/6323b1f8b217780454c61304bcd2b38ae4762f50754414124603ccc90bb2/pydantic_core-2.46.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff", size = 1922768, upload-time = "2026-08-28T09:58:49.58Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a3/c05ca796e1197618a774b01e596aeedfefc2f7d8c01ae3054e910b120e8a/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:193375f3548919d3f0b60936ca113ada3e38f264f91b9b8e0508efaad57be931", size = 1951241, upload-time = "2026-08-28T09:58:51.511Z" }, + { url = "https://files.pythonhosted.org/packages/68/32/33bc39ac705c52cffc908e8389f9754fdb208aea5c69cceddf4eb3ce99af/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79bdfa52f843137045b2d081cc05c120ba6665d29b7559c2c47690906f39279f", size = 2031975, upload-time = "2026-08-28T09:58:53.166Z" }, + { url = "https://files.pythonhosted.org/packages/b0/70/2333e885c0f6a67bc105c5916965dac9b57f2718ee20d81d1a06a4ebdc13/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24922243639cbdac66c75fcb6fd6495a9cb52b213d62f9a0d16f0310b1ff8038", size = 2208542, upload-time = "2026-08-28T09:58:55.017Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ea/296debfb4264207bbda5936133892e027c0a58875ad53ebd512fba8ec3a2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76fe65e607be28c7fd4d56fc3c42b1583aa058ce3408b7ad0fd540171d31f9f", size = 2264692, upload-time = "2026-08-28T09:58:56.767Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/9e4de77a6271e07a76d2d58b11c091a979c191ed2939bf80067568b369d2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f7b393a8b3da82f5c1fc0751e6d01ac6c55b93c18226a60bdfba4a724efafd1", size = 2066633, upload-time = "2026-08-28T09:58:58.531Z" }, + { url = "https://files.pythonhosted.org/packages/8d/db/f9e9d0c97445987b2084823d5c240de88087338f04fc2cfaa2df186b8049/pydantic_core-2.46.5-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:7ac031912d54f3d83ef3b3eb98dfabc1608802e2202263d25957eeed40b94761", size = 2105235, upload-time = "2026-08-28T09:59:00.421Z" }, + { url = "https://files.pythonhosted.org/packages/07/c5/79169b047b3b2c3e99e04bc76372af9637e0bf6db638274fa927df96369e/pydantic_core-2.46.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:837b396ca3d7b74091ca623f6cbd8351bd42d670a79c2683e79fb089f06a2de5", size = 2157367, upload-time = "2026-08-28T09:59:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/26/b5/ba6057afb7c291bd449f51b867f95aef2072941c4ce4e5c31d6ffd132d3b/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:5ee239d575f80b08eca11f6e20f90c4c695de7825c67eefe6091fbf20dda648e", size = 2158420, upload-time = "2026-08-28T09:59:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/6e/28/2057abecaafdc22912afa819603a51f0a62d40643b7c4871c51721fea9be/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e80675d75ae2cd14372cb65cad5400d9347a3d3f6c13000183f22dfd027283ed", size = 2309588, upload-time = "2026-08-28T09:59:06.048Z" }, + { url = "https://files.pythonhosted.org/packages/71/9d/881156dc404e27479c4246128d73538464cab4a239bec61995e227644c30/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c4b71f10dd532fb7a5cbc8f58707779e64f03a258c2bf8bfbaecfcd9970b519", size = 2341866, upload-time = "2026-08-28T09:59:08.539Z" }, + { url = "https://files.pythonhosted.org/packages/5a/38/d66f443a259f84d13babdceae568e572b0ed26da17ca5d0a649ebb110a67/pydantic_core-2.46.5-cp313-cp313-win32.whl", hash = "sha256:97bf8de4d541598c94a59344eeb988a94c08ff76b5723c41f6567ec18c7892ea", size = 1938580, upload-time = "2026-08-28T09:59:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1e/1d5371213f4cc9a7ed70c0bfcc7911de22311ee99a662a56077d7292d2ac/pydantic_core-2.46.5-cp313-cp313-win_amd64.whl", hash = "sha256:15f4a94963c95accac15b7b657bb177d3ad82bb90b0d0526d9a9b85079925db5", size = 2041980, upload-time = "2026-08-28T09:59:12.396Z" }, + { url = "https://files.pythonhosted.org/packages/5a/48/4222d90b1c67568bace4dec6dca6271449c66de3595d72b6d098f5fde597/pydantic_core-2.46.5-cp313-cp313-win_arm64.whl", hash = "sha256:d22a945598fb91236b4dd793a6e42e4f3dd7740bb5aace5ebd7d4c08d13bb575", size = 1997213, upload-time = "2026-08-28T09:59:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8a/14596f2a8367da50cf7cbac48169ee5d9c8e11d486a3b527082384630c72/pydantic_core-2.46.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c1c43ad4339643d70ebb8124e1305a7dab423001eff58bb41a0f731adbc98355", size = 2074081, upload-time = "2026-08-28T09:59:16.141Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d5/d8a4eb6d6c7f66b91dd37c576d76e9e60fba900caf5372c17bcf949febc2/pydantic_core-2.46.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1a353f84de772f423b5ffb11d7ae352fbbef0f446f3c0b0af0f8236d7233606e", size = 1920497, upload-time = "2026-08-28T09:59:18.065Z" }, + { url = "https://files.pythonhosted.org/packages/8e/26/092079428f86e927e030b2c0ced87df69dbb1c875cdeaa67bf42ea2be746/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5086029a57366b8cf81b130a43908738095c270c21a8d7f0e8bdfdb89718e2f3", size = 1952130, upload-time = "2026-08-28T09:59:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/8ec0e290a9ebaebd64047bf5fda94be835c6b1551b02437e4b76778fbcd7/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46c25dda9d092a06c08db76ffe0a197107904d0dfac653f7d5306bbcd6d6119c", size = 2026371, upload-time = "2026-08-28T09:59:22.227Z" }, + { url = "https://files.pythonhosted.org/packages/01/72/4fd20ad520fb8da0157f95b27a7eb05a72790ef08138e7701ac972c342ea/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37ea7b83c935e5b0d68c9449b82651accf78a10828b2c02b2f2d9e9496446c21", size = 2202822, upload-time = "2026-08-28T09:59:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/31/b0/d16e0771206b29314f0d52198b720be21e8a99ab2bf11e3bc0d7c9cebdff/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64e88d5585bea9ce95861079de72006c7fa6d3df4e3a3b65ba31eb979c15c9f", size = 2262756, upload-time = "2026-08-28T09:59:26.608Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9b/59634b7ac631c63b2a37760eb6943af3e29573d6b59a4abc5e7f019d4cee/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d510bac3ee52247af28ed4bb18a1e799f040ac60fd2bf5ccd4c92f1fbe786f", size = 2068352, upload-time = "2026-08-28T09:59:29.044Z" }, + { url = "https://files.pythonhosted.org/packages/08/7c/570abb1ad2155348dc754ea91be22e5aaa18eb6d69a6068f7c6f2679a6ed/pydantic_core-2.46.5-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a2a5e1d0ff29adddc9f6d6821a66302e4493f8ca898b715b6b1182c2c201ea0a", size = 2104777, upload-time = "2026-08-28T09:59:30.95Z" }, + { url = "https://files.pythonhosted.org/packages/8e/25/5bf74adc65a1ac5b7be3f6cb0bcb5433615c1598a801c19d830d84c98ded/pydantic_core-2.46.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03b9666e41e35d8909852ba191a0607520f81b74eaf12ccf8737005dbb313821", size = 2156312, upload-time = "2026-08-28T09:59:32.604Z" }, + { url = "https://files.pythonhosted.org/packages/90/6a/2ef38830675e050121040618135564ed56b860b45433b02d9b4ebece46f3/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:a91c17edf6eea2402cb5457b4c89e99bc5ed1004aa34c4adf1d4258c1a5c22c2", size = 2150067, upload-time = "2026-08-28T09:59:34.453Z" }, + { url = "https://files.pythonhosted.org/packages/90/ef/a7dbb03a14a64c2a4621f989c615ed9a892535a6cad938fc27079f919d80/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b49924c73a235e969511bf2aabdff3beebf9820931f646c80274d5d780010c47", size = 2304516, upload-time = "2026-08-28T09:59:36.194Z" }, + { url = "https://files.pythonhosted.org/packages/68/f8/6bb4c4b80e8a6fde1904c64a51c62a1d04fcdfa3ea521a66b2ddefa1d885/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:2cbd9a5eff05e51c447c34dfa4632145b26b09120cf04bd0c871e44c1a5e1c9a", size = 2335223, upload-time = "2026-08-28T09:59:37.931Z" }, + { url = "https://files.pythonhosted.org/packages/2a/80/f46b8c681195190b2c1f1c7c0a81abce60663e987613e09ef64d433dd96b/pydantic_core-2.46.5-cp314-cp314-win32.whl", hash = "sha256:2d5d76654becf5efd62c9e51c3756c67b49498b0c9a40884934c40807adbd074", size = 1934827, upload-time = "2026-08-28T09:59:39.836Z" }, + { url = "https://files.pythonhosted.org/packages/f7/3c/60674207246bc0a4009d2391b7c7251c7159f279c8d2ab8aae8ef46f3dee/pydantic_core-2.46.5-cp314-cp314-win_amd64.whl", hash = "sha256:fa10ef4112775900e7a0661068635eb67b2ab824fbde764de6e0e21982a93db0", size = 2042648, upload-time = "2026-08-28T09:59:41.792Z" }, + { url = "https://files.pythonhosted.org/packages/69/0c/117c562c7c1babdf44576b72a5e496906506c93690387ecfbca7c729ae2e/pydantic_core-2.46.5-cp314-cp314-win_arm64.whl", hash = "sha256:045ab3b6d308439e32b81cc173bba5b9018bc6ed896afd0c65b3b009b1699af5", size = 1989652, upload-time = "2026-08-28T09:59:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/e8/66/9336ae58f9eb68c41d121894e52c4c89eccb07eb8f602a04ee9c3f37736a/pydantic_core-2.46.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8816f3d218beb4b787de5c9759c259b8fa61f9dec42dc7811f320a33771778b7", size = 2065829, upload-time = "2026-08-28T09:59:45.364Z" }, + { url = "https://files.pythonhosted.org/packages/c5/02/bc19b47a96c2d3109760711acf22369e56bd7e405ca52f7ade164d2ead57/pydantic_core-2.46.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bce57638e08ac148e5778cce7feb968307a727d66f8e2274a543d0cf0c9ad6a3", size = 1905716, upload-time = "2026-08-28T09:59:47.18Z" }, + { url = "https://files.pythonhosted.org/packages/52/a4/70b47c0509923dd98ccfed04fb3e32ea3849c82a0ff2205bb41009b43c00/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976e1128455aa595ea04c79ccfedff1aaeab96ee013fcc916bed120c4f0ad94f", size = 1934216, upload-time = "2026-08-28T09:59:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/52/ab/aa03b65f7bb198585edf806b906c3223ecf1795543e39e23aec4cce27ad2/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b891faeedeafba41b2983e5001a81b6a915b69544c7e7570d1989ce1c36ac7", size = 2010635, upload-time = "2026-08-28T09:59:51.692Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8b/0da06343f30b84ec549aafd309c6456223d5dc8bd36af504c573faad561d/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f194189415698233dd1114a093a9b56e61e2c57e11b469be3b0506f46f0771c", size = 2209369, upload-time = "2026-08-28T09:59:53.582Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5b/844c4defaa34a3df66eb9257087d121d70c201298b96abdf9f492fc2f1bf/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82a36973cf8a2ef5406f4fe2edbf8ed0c99629535d959e0b100c76a32535a111", size = 2253238, upload-time = "2026-08-28T09:59:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/f4/64/a4e536cb16d7f61a7fd3120b46c577fc7fa7325992f69c4f52bc786d77d8/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbb78909f52b981d3b2d56b97328d71eb0b974c36bd77c920123a7ebb192829", size = 2065740, upload-time = "2026-08-28T09:59:58.038Z" }, + { url = "https://files.pythonhosted.org/packages/5f/75/aaa38c6bc2d085f6605b34eabdc6a8a4e0b2e61fc9c8e6e52b28e97b3125/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:52e24eacdb536cade636aa90fb851835222becff8484b7001fdc78cb0290f2aa", size = 2087425, upload-time = "2026-08-28T09:59:59.898Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/fcab4cfc39aba3689e1d20c8b5250ad280957022c09af2ed9cd585602a5e/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37ae34309d7bd8c0d61ab839668058f2a7962ea1fc51d105d2db228fe0618034", size = 2139306, upload-time = "2026-08-28T10:00:03.057Z" }, + { url = "https://files.pythonhosted.org/packages/2d/f4/f1d03a4bc9d9acbc62f4d742b8a319af52f71885079868b2ff8e48a651ee/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:0cdbada856a1c69a7624a64d3d9aefe79300bd6ef827b43a4f265010b9b55184", size = 2144589, upload-time = "2026-08-28T10:00:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/83/f3/7a53bb1356de514a4cd295f25b6ac39237895620c0462d2592b76c16e114/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:545f26c504b27c3758439a5e6d9349931f0a04f855668d5fe323c89e82300a38", size = 2288882, upload-time = "2026-08-28T10:00:07.931Z" }, + { url = "https://files.pythonhosted.org/packages/cd/94/5a81583660c175c59d49ffb09f4b3a44debeaf86a19fca664ae1cdd9ee32/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9", size = 2335210, upload-time = "2026-08-28T10:00:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9f/5d685c2693b972d1a59c998586e8823712b66603aeff47ee60a4bdaafd37/pydantic_core-2.46.5-cp314-cp314t-win32.whl", hash = "sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9", size = 1921180, upload-time = "2026-08-28T10:00:12.35Z" }, + { url = "https://files.pythonhosted.org/packages/70/12/5c94ee16d65a37a15f9e869f5e6256df111154491173801a4c5e800ab548/pydantic_core-2.46.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290", size = 2020515, upload-time = "2026-08-28T10:00:14.774Z" }, + { url = "https://files.pythonhosted.org/packages/63/19/67830dda664e6bdf9285ee2e40f355d0d7d6b92aa0c42e8d217bb8d33d36/pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f", size = 1989276, upload-time = "2026-08-28T10:00:16.984Z" }, + { url = "https://files.pythonhosted.org/packages/df/dd/053c2e4303f791f3b8f8a14ab0b22008e8eb21d868c0c90b4f9be705b76a/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:013d6f3483d81e02e7c328831808f336c8596ee33b4bd4026b9ffb1e960b8942", size = 2062540, upload-time = "2026-08-28T10:01:00.318Z" }, + { url = "https://files.pythonhosted.org/packages/d7/dd/a18df751a5e37dd51bfad7f68e766999125bebe68c9e1d10a493ad01bd63/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:e9c134bb666dd54b778b9fc0d2b50cbb7f979b9e3716f26a88c9ab3b6fc1dd0f", size = 1902040, upload-time = "2026-08-28T10:01:02.529Z" }, + { url = "https://files.pythonhosted.org/packages/b7/13/01d40f9d07ce8a779fd6e0bd8ad4fba91309500dd67b869e2e219d261a6d/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:347ec774390c87326a2e4929d58d3f7e8763a104d5d35f4cd595a4c952366433", size = 1967479, upload-time = "2026-08-28T10:01:05.004Z" }, + { url = "https://files.pythonhosted.org/packages/fa/04/c81d4841331c2178b6fb09ae225425e110ed72d990c9fe556c4ec03d1013/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e24d8f05fa2d28513d94e877e9c75ad66175376209b3977f916e240e623193c", size = 2111034, upload-time = "2026-08-28T10:01:07.345Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyright" +version = "1.1.411" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/ab/265f7dc69d28113ebba19092e57b075f41543b2ed048429c5f56e2b88eac/pyright-1.1.411.tar.gz", hash = "sha256:d885a0551f2e763b089a02702174e7f4ba77548cddabc972ab86d1f7f1b0f998", size = 4112861, upload-time = "2026-06-25T02:14:06.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/49/385be530a6a5b78d1cbcd5c2e38debc8959a2fc6bdb716f4e581002979fc/pyright-1.1.411-py3-none-any.whl", hash = "sha256:dc7c72a8e2700c55baa127554040e067041ea53ccfd50bf96308cc4291c7d5d9", size = 6181526, upload-time = "2026-06-25T02:14:04.691Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "pytest-timeout" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" }, + { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" }, + { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" }, + { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" }, + { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" }, + { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510, upload-time = "2026-06-30T07:15:57.921Z" }, + { url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495, upload-time = "2026-06-30T07:15:59.238Z" }, + { url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454, upload-time = "2026-06-30T07:16:01.021Z" }, + { url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583, upload-time = "2026-06-30T07:16:02.287Z" }, + { url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919, upload-time = "2026-06-30T07:16:03.723Z" }, + { url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725, upload-time = "2026-06-30T07:16:05.305Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255, upload-time = "2026-06-30T07:16:07.086Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060, upload-time = "2026-06-30T07:16:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960, upload-time = "2026-06-30T07:16:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356, upload-time = "2026-06-30T07:16:11.816Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319, upload-time = "2026-06-30T07:16:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508, upload-time = "2026-06-30T07:16:15.23Z" }, + { url = "https://files.pythonhosted.org/packages/87/03/2a69ab618a789cf6cf85c86bb844c62d090e700ab1a2aa676b3741b6c516/rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8", size = 202504, upload-time = "2026-06-30T07:16:16.893Z" }, + { url = "https://files.pythonhosted.org/packages/85/62/a3892ba945f4e24c78f352e5de3c7620d8479f73f211406a97263d13c7d2/rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5", size = 220380, upload-time = "2026-06-30T07:16:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e7/c2bd44dc831931815ad11ebb5f430b5a0a4d3caa9de837107876c30c3432/rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703", size = 215976, upload-time = "2026-06-30T07:16:19.654Z" }, + { url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840, upload-time = "2026-06-30T07:16:21.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282, upload-time = "2026-06-30T07:16:22.875Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403, upload-time = "2026-06-30T07:16:24.415Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055, upload-time = "2026-06-30T07:16:26.111Z" }, + { url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419, upload-time = "2026-06-30T07:16:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848, upload-time = "2026-06-30T07:16:29.183Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369, upload-time = "2026-06-30T07:16:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673, upload-time = "2026-06-30T07:16:32.486Z" }, + { url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500, upload-time = "2026-06-30T07:16:34.471Z" }, + { url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978, upload-time = "2026-06-30T07:16:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350, upload-time = "2026-06-30T07:16:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486, upload-time = "2026-06-30T07:16:39.797Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/21101359743cd136ada781e8210a85769578422ba460672eea0e29739200/rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e", size = 201068, upload-time = "2026-06-30T07:16:41.316Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b2/9574d4d44f7760c2aa32d92a0a4f41698e33f5b204a0bf5c9758f52c79d5/rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2", size = 220600, upload-time = "2026-06-30T07:16:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726, upload-time = "2026-06-30T07:16:44.5Z" }, + { url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587, upload-time = "2026-06-30T07:16:46.255Z" }, + { url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585, upload-time = "2026-06-30T07:16:48.101Z" }, + { url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479, upload-time = "2026-06-30T07:16:49.93Z" }, + { url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418, upload-time = "2026-06-30T07:16:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123, upload-time = "2026-06-30T07:16:53.622Z" }, + { url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351, upload-time = "2026-06-30T07:16:55.241Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827, upload-time = "2026-06-30T07:16:56.841Z" }, + { url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966, upload-time = "2026-06-30T07:16:58.557Z" }, + { url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680, upload-time = "2026-06-30T07:17:00.164Z" }, + { url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853, upload-time = "2026-06-30T07:17:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715, upload-time = "2026-06-30T07:17:03.693Z" }, + { url = "https://files.pythonhosted.org/packages/29/71/4d8fcf700931815594bce892255bbd973b94efaf0fc1932b0590df18d886/rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369", size = 202864, upload-time = "2026-06-30T07:17:05.746Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/b577562de0edbb55b2be85ce5fd09c33e386b9b13eee09833af4240fd5c4/rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146", size = 220430, upload-time = "2026-06-30T07:17:07.471Z" }, + { url = "https://files.pythonhosted.org/packages/c8/95/d6d0b2509825141eef60669a5739eec88dbc6a48053d6c92993a5704defe/rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e", size = 215877, upload-time = "2026-06-30T07:17:09.008Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933, upload-time = "2026-06-30T07:17:10.762Z" }, + { url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274, upload-time = "2026-06-30T07:17:12.266Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763, upload-time = "2026-06-30T07:17:14.107Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467, upload-time = "2026-06-30T07:17:15.76Z" }, + { url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689, upload-time = "2026-06-30T07:17:17.308Z" }, + { url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340, upload-time = "2026-06-30T07:17:18.928Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179, upload-time = "2026-06-30T07:17:20.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993, upload-time = "2026-06-30T07:17:22.212Z" }, + { url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909, upload-time = "2026-06-30T07:17:23.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584, upload-time = "2026-06-30T07:17:25.264Z" }, + { url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357, upload-time = "2026-06-30T07:17:26.888Z" }, + { url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533, upload-time = "2026-06-30T07:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/ba/76/65002b08596c389105720a8c0d22298b8dc25a4baf89b2ce431343c8b1de/rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577", size = 201204, upload-time = "2026-06-30T07:17:30.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/97/d855d6b3c322d1f27e26f5241c42016b56cf01377ea8ed348285f54652f0/rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324", size = 220719, upload-time = "2026-06-30T07:17:31.788Z" }, +] + +[[package]] +name = "ruff" +version = "0.16.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/7c/6adb35d70e7c027e308274557901c7e00fb3407750faf3620c184ae058cb/ruff-0.16.6.tar.gz", hash = "sha256:dcf8a73d2ff77e99dde91244b4da16feba7f14e6beeb4015dee7c5a909e99050", size = 4921251, upload-time = "2026-09-03T16:57:29.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/28/9cc1b79639e284ec103f43c88c644db4eb58cbd0ea1ca11f1193435369ac/ruff-0.16.6-py3-none-linux_armv6l.whl", hash = "sha256:61c368c26bf8e973e5ab14a2772de587bc068ea3f9a277f673380749b4898fb8", size = 10015638, upload-time = "2026-09-03T16:56:40.986Z" }, + { url = "https://files.pythonhosted.org/packages/71/11/627d342ef727ea7794edf74fe23d60a074b02c3acc2e9436684e782286ca/ruff-0.16.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ecf4f068e2e123e43a26e9db4e19524cc56563912404e83bbfca375757e45a32", size = 10220762, upload-time = "2026-09-03T16:56:44.681Z" }, + { url = "https://files.pythonhosted.org/packages/43/d9/b75668ce41e4c8d073d18d6d08672ba6906ce45d5c06ea4fdb2e84ce3853/ruff-0.16.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:99b62ea33baf130f50368798d841f0d95527b6d817bf31817b65dd058f1d314c", size = 9835082, upload-time = "2026-09-03T16:56:47.142Z" }, + { url = "https://files.pythonhosted.org/packages/99/97/123ab10b05cde889c107c20f5a9774955104b5552796a2a8584b089ae8eb/ruff-0.16.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fbf89013f2bb3f6835a6038ff658dc8a1b38c98dc8e724b964168ad4e881876", size = 9949304, upload-time = "2026-09-03T16:56:49.813Z" }, + { url = "https://files.pythonhosted.org/packages/3e/58/a4a2c59dd2e5b85929c912d9cac3056eb9ee8c7e75e9b9fe3e109174966b/ruff-0.16.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56a67065e22efa6bc4d498299d3bb06c0c90aace8fac2068b5a12f9dc4d8d51d", size = 9840612, upload-time = "2026-09-03T16:56:52.368Z" }, + { url = "https://files.pythonhosted.org/packages/61/6a/ff8c8626a786c4f49d48ced4a752dadbca65f5263005f9c2416578194694/ruff-0.16.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e25cc89174874b176a157e4428d66761c2c0c006654419bf384f967f361ff1b1", size = 10543465, upload-time = "2026-09-03T16:56:55.089Z" }, + { url = "https://files.pythonhosted.org/packages/ad/bb/c47535923365f337b82e28192e4e9eef2176511007cfd99a62fc22df5dad/ruff-0.16.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700580ed5303723cb3c11c2f1d2a8913ce77b7ea86646dddb887f5417a9ba70", size = 11267576, upload-time = "2026-09-03T16:56:57.791Z" }, + { url = "https://files.pythonhosted.org/packages/ba/50/e5119a5212b5cd63b51e1f4b25e7bd636a6668fc069a3160b108ad7e3c16/ruff-0.16.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15f1d0b6e165a6e56567befb6629f8209271311d990bae0f37e6d065035ef5f3", size = 10781993, upload-time = "2026-09-03T16:57:00.666Z" }, + { url = "https://files.pythonhosted.org/packages/8b/98/083d8b4ef3c51a0d19db84367791cbe9f44e4b53343d19dfa83556e1cd9a/ruff-0.16.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72c591a96986ee4268860e2b7235082129ca5e4cb9cbba653a4b57c11893757", size = 10317748, upload-time = "2026-09-03T16:57:03.428Z" }, + { url = "https://files.pythonhosted.org/packages/9a/29/68f7ff2c5ad95f19f00627ac2de95644e25fe47371ea60b2db1fd952315e/ruff-0.16.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:65a006baa18f33324325814c864daef03541d51564b98c517610ea756ab7003e", size = 10540096, upload-time = "2026-09-03T16:57:06.182Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f9/79a8f6de85968641d68a7863aeec577551924ef066a990a48ff93167beab/ruff-0.16.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cd02a7bf1a21a8735228a3e8c95a9dc5cf86bd2a52194f4aaae2a5755b4de0f4", size = 10100494, upload-time = "2026-09-03T16:57:09.194Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e8/b81a22d9b90c00b892ccf2fa2ac36fa95de4c13ab85aea3e73795cfe4651/ruff-0.16.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:31b36f1e5ad85e0737f09d2be4e512e2e283583c14015da3b9dc07359ac0fc88", size = 9843663, upload-time = "2026-09-03T16:57:12.168Z" }, + { url = "https://files.pythonhosted.org/packages/39/aa/54f516ec5e5a11c4afdceb1c454ebb054ffb96e4f4a1705580b4346abd35/ruff-0.16.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:61029b4ab4aa723fd3064fab96b1d814492596bf0c792679fffcbde1e1679953", size = 10282461, upload-time = "2026-09-03T16:57:15.077Z" }, + { url = "https://files.pythonhosted.org/packages/52/0b/38d0aa8aa32372b96dc44f97b22e576c4147808271aab7b2cb1e353d4445/ruff-0.16.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ac8998457832c2061709d900856b7ad271dace0cb41f346588d540162bfa718", size = 10728808, upload-time = "2026-09-03T16:57:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e5/9e274e24eeb027640ffc7442f21239f16d17f47acec15ae34f32e03a5c79/ruff-0.16.6-py3-none-win32.whl", hash = "sha256:0b87d9d16fcb63e8018423ca1d50b7260f15cb2da33e30db4baad4183a948c25", size = 10049212, upload-time = "2026-09-03T16:57:20.55Z" }, + { url = "https://files.pythonhosted.org/packages/22/31/72472449414223ed1a2da236b992adbb1a2ae59e34794574810f60ce068e/ruff-0.16.6-py3-none-win_amd64.whl", hash = "sha256:10d21c51c3495d8eaea7b703a16592117ea6eb1d649e36335aa965ff1173eb39", size = 10556402, upload-time = "2026-09-03T16:57:23.501Z" }, + { url = "https://files.pythonhosted.org/packages/fc/07/d781f8f8e1ac24bef9f3269cf62ffb1407ca24c3a8f12e5e22874f90528c/ruff-0.16.6-py3-none-win_arm64.whl", hash = "sha256:7a976c79b958f94e50a022a19f0f8c87387448020935ec14fc74331bd0a7f2c5", size = 10412850, upload-time = "2026-09-03T16:57:26.416Z" }, +] + +[[package]] +name = "starlette" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/b4/205b0d5241d934e8add0c38aa924c4f9fb7330834ff11e5444db964ec3f9/starlette-1.6.0.tar.gz", hash = "sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b", size = 2716969, upload-time = "2026-08-08T18:27:57.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.52.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/0f/3f86e61397dd33bf2ccf28188c40db6a740658aeebbbf6e7dbc101a1f487/uvicorn-0.52.4.tar.gz", hash = "sha256:73acfee47a0b133c5de13d219492d62d8a31e935f4fe6e41a232451a15379f86", size = 100627, upload-time = "2026-08-19T06:27:41.821Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/79/4a20b54ab0491485ccd8c077db2d39187c7f12b3e15485d38a7be37c81b4/uvicorn-0.52.4-py3-none-any.whl", hash = "sha256:f86e41a149d7d05a9969337e3946a9c171c06a5d42680896daaba624aeac8da1", size = 79871, upload-time = "2026-08-19T06:27:40.36Z" }, +] diff --git a/web/eslint.config.js b/web/eslint.config.js new file mode 100644 index 0000000..5bf5a8a --- /dev/null +++ b/web/eslint.config.js @@ -0,0 +1,16 @@ +import js from "@eslint/js"; +import reactHooks from "eslint-plugin-react-hooks"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist", "node_modules"] }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ["**/*.{ts,tsx}"], + plugins: { "react-hooks": reactHooks }, + rules: { + ...reactHooks.configs.recommended.rules, + }, + }, +); diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..3e25ca9 --- /dev/null +++ b/web/index.html @@ -0,0 +1,13 @@ + + + + + + Muse 工作台 + + +
+ + + + diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..6182e2d --- /dev/null +++ b/web/package.json @@ -0,0 +1,46 @@ +{ + "name": "muse-workbench", + "private": true, + "version": "0.1.0", + "type": "module", + "description": "Muse 作者工作台(React/TS);业务页面随工作包接入真实后端", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "test": "vitest run", + "test:watch": "vitest", + "lint": "eslint src", + "typecheck": "tsc -b --noEmit" + }, + "dependencies": { + "@tanstack/react-query": "^5.102.8", + "@tiptap/extension-unique-id": "3.31.3", + "@tiptap/pm": "3.31.3", + "@tiptap/react": "3.31.3", + "@tiptap/starter-kit": "3.31.3", + "openapi-fetch": "^0.17.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.18.3" + }, + "devDependencies": { + "@eslint/js": "9.15.0", + "@playwright/test": "^1.49.0", + "@testing-library/react": "^16.3.3", + "@testing-library/user-event": "^14.6.7", + "@types/node": "22.20.1", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.15.0", + "eslint-plugin-react-hooks": "^5.0.0", + "jsdom": "^26.1.0", + "openapi-typescript": "^7.13.0", + "prettier": "^3.9.6", + "typescript": "^5.6.3", + "typescript-eslint": "^8.15.0", + "vite": "^6.0.1", + "vitest": "^2.1.5" + } +} diff --git a/web/playwright.config.ts b/web/playwright.config.ts new file mode 100644 index 0000000..07238ab --- /dev/null +++ b/web/playwright.config.ts @@ -0,0 +1,16 @@ +import { defineConfig, devices } from "@playwright/test"; + +// 真实浏览器旅程:对真后端与隔离库执行中文输入与实际点击。 +// 用例随 P2 起逐包登记在 web/tests/旅程/;此前本配置只登记环境合同。 +export default defineConfig({ + testDir: "./tests/旅程", + timeout: 60_000, + retries: 0, + use: { + baseURL: process.env.MUSE_WORKBENCH_URL || "http://127.0.0.1:5173", + locale: "zh-CN", + trace: "retain-on-failure", + launchOptions: {executablePath: process.env.MUSE_BROWSER_EXECUTABLE}, + }, + projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }], +}); diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml new file mode 100644 index 0000000..1c47476 --- /dev/null +++ b/web/pnpm-lock.yaml @@ -0,0 +1,3849 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tanstack/react-query': + specifier: ^5.102.8 + version: 5.102.8(react@18.3.1) + '@tiptap/extension-unique-id': + specifier: 3.31.3 + version: 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/pm': + specifier: 3.31.3 + version: 3.31.3 + '@tiptap/react': + specifier: 3.31.3 + version: 3.31.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tiptap/starter-kit': + specifier: 3.31.3 + version: 3.31.3 + openapi-fetch: + specifier: ^0.17.0 + version: 0.17.0 + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-router-dom: + specifier: ^7.18.3 + version: 7.18.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + devDependencies: + '@eslint/js': + specifier: 9.15.0 + version: 9.15.0 + '@playwright/test': + specifier: ^1.49.0 + version: 1.63.0 + '@testing-library/react': + specifier: ^16.3.3 + version: 16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/user-event': + specifier: ^14.6.7 + version: 14.6.7(@testing-library/dom@10.4.1) + '@types/node': + specifier: 22.20.1 + version: 22.20.1 + '@types/react': + specifier: ^18.3.12 + version: 18.3.31 + '@types/react-dom': + specifier: ^18.3.1 + version: 18.3.7(@types/react@18.3.31) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.7.0(vite@6.4.3(@types/node@22.20.1)) + eslint: + specifier: ^9.15.0 + version: 9.39.5 + eslint-plugin-react-hooks: + specifier: ^5.0.0 + version: 5.2.0(eslint@9.39.5) + jsdom: + specifier: ^26.1.0 + version: 26.1.0 + openapi-typescript: + specifier: ^7.13.0 + version: 7.13.0(typescript@5.9.3) + prettier: + specifier: ^3.9.6 + version: 3.9.6 + typescript: + specifier: ^5.6.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.15.0 + version: 8.70.0(eslint@9.39.5)(typescript@5.9.3) + vite: + specifier: ^6.0.1 + version: 6.4.3(@types/node@22.20.1) + vitest: + specifier: ^2.1.5 + version: 2.1.9(@types/node@22.20.1)(jsdom@26.1.0) + +packages: + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.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/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.7': + resolution: {integrity: sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.15.0': + resolution: {integrity: sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@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.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + + '@playwright/test@1.63.0': + resolution: {integrity: sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==} + engines: {node: '>=20'} + hasBin: true + + '@redocly/ajv@8.11.2': + resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} + + '@redocly/config@0.22.0': + resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==} + + '@redocly/openapi-core@1.34.19': + resolution: {integrity: sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.63.1': + resolution: {integrity: sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.63.1': + resolution: {integrity: sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.63.1': + resolution: {integrity: sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.63.1': + resolution: {integrity: sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.63.1': + resolution: {integrity: sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.63.1': + resolution: {integrity: sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': + resolution: {integrity: sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.63.1': + resolution: {integrity: sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.63.1': + resolution: {integrity: sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.63.1': + resolution: {integrity: sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.63.1': + resolution: {integrity: sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.63.1': + resolution: {integrity: sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.63.1': + resolution: {integrity: sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.63.1': + resolution: {integrity: sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.63.1': + resolution: {integrity: sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.63.1': + resolution: {integrity: sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.63.1': + resolution: {integrity: sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.63.1': + resolution: {integrity: sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.63.1': + resolution: {integrity: sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.63.1': + resolution: {integrity: sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.63.1': + resolution: {integrity: sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.63.1': + resolution: {integrity: sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.63.1': + resolution: {integrity: sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.63.1': + resolution: {integrity: sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.63.1': + resolution: {integrity: sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==} + cpu: [x64] + os: [win32] + + '@tanstack/query-core@5.102.8': + resolution: {integrity: sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==} + + '@tanstack/react-query@5.102.8': + resolution: {integrity: sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==} + peerDependencies: + react: ^18 || ^19 + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/react@16.3.3': + resolution: {integrity: sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@testing-library/user-event@14.6.7': + resolution: {integrity: sha512-MPCpX8bxe8zS+JmmTwLp8jd0dy1rAm60Te/SL8JrQM3qvQJcBOs1d7IefJMyZzqM3EWBrDn/LWDt1BCGu4ASfg==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tiptap/core@3.31.3': + resolution: {integrity: sha512-Cz50pvciQrxdSxgTkHOVz0uD0Yl/8Xt0QatGD6ILm47jW8EzyHR9RkUGs/D5IqzXKuVPntfw1ttaT926vXfiRg==} + peerDependencies: + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-blockquote@3.31.3': + resolution: {integrity: sha512-fyY2XMbyDDDfOTQ1Qdrnqa1qwC9DWE4n7AfE0EKQI0G8MfLV8RaDlLDcOZDJ9JbMPY7/Gx7EjyK4NKxSW9n2hQ==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-bold@3.31.3': + resolution: {integrity: sha512-dIuYhKk8TitKU/FeDpoTeWZhU42YgDN5npgWNjAmMmRktPdoxnH3/wGSiwXlqZgJWjehN7kPWDePWpJeAImGpQ==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-bubble-menu@3.31.3': + resolution: {integrity: sha512-EV6ZnwKc++2OM/OcD54n8s1B7C9LP7GKAtdEPwu0t3BYJf3sG8Ueoinf7SgGPO9oMPg96GneOkDNm1urMV167g==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-bullet-list@3.31.3': + resolution: {integrity: sha512-qEyyoPapPef4LO8XKaN83bxtaNzkJ4kFn/IxLnEKd4BJ3Mvi4MH2yJYlyDqwFnMoev4pMA1zBHRAt/C0THRmZw==} + peerDependencies: + '@tiptap/extension-list': 3.31.3 + + '@tiptap/extension-code-block@3.31.3': + resolution: {integrity: sha512-nvknt4FhyJQjYcvxptmeUlFsIAc8ibua3E5BN4Pim374/9RWepH4cdE9X0/qUTtguHElLx0iKtSIY3rW2qGTFA==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-code@3.31.3': + resolution: {integrity: sha512-SzxOqchrD2AcN3uT67PjKmRFEMOU3vNNiwNaamZJUbZrI6Hmy+bvdHJrc3jrIddyyCrAtsnAfRI0cmorW1jGfg==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-document@3.31.3': + resolution: {integrity: sha512-EexgmqnyDNyGlISxo7SMrp5MygpJYmqD+0cY5jB6L1U6L4CpKKRWUt8OO1sWzEHDE1+TTvwt+WIFoIWAziOtEA==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-dropcursor@3.31.3': + resolution: {integrity: sha512-NWomSfu5CSC7VacnMSDzKT8qm66SzMfZwVPEtwY5bPpRTJgTiT1rNK0neDrrzfMN27MfylGyKWWf7Q5Qf8w/fg==} + peerDependencies: + '@tiptap/extensions': 3.31.3 + + '@tiptap/extension-floating-menu@3.31.3': + resolution: {integrity: sha512-rd4VJ9PGSP9Eop8ZTEwaLZcMzMXLuJKe3hUNf58rq+zANpwM+9fI+vB7g9MAc3eXwUNDxNDVACFIL2oeBqpQ3A==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-gapcursor@3.31.3': + resolution: {integrity: sha512-EBXKb1FrVStsNYCcRGtd9jmzveCvR+eqgg1rVqoONrqFK6U7bga6LN+1dMKroP1kliDVgveYkP3vRYxqw+rFqg==} + peerDependencies: + '@tiptap/extensions': 3.31.3 + + '@tiptap/extension-hard-break@3.31.3': + resolution: {integrity: sha512-QAdCvNO4+yW9ATwsrej11NTkDYFqPLIEQr3ARNrKOK1qaiS7A0fia2SEukb/hrkP3A6mbozhoQt2r2RGUf/DpQ==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-heading@3.31.3': + resolution: {integrity: sha512-rk5VHMAeQcg06SLauN6EGdD2jc0O2qY8QkZYPd0LxNvLblw2BxBx+lxUQSYwLAT9Ie5914gKIK2YbRyO2Ts3ig==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-horizontal-rule@3.31.3': + resolution: {integrity: sha512-YnHGy2KShRwvCseAmmxl9VP7R0qaj8QMp3DA6DJWZqp7r5gLGvDkAqhedxqqefqsE4Y43hjkBdjtB9Ce78LkIw==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-italic@3.31.3': + resolution: {integrity: sha512-ibGvdvAPyfxBMUVNRI43eb9h2/Jka1MRG5GtnGqbcCX/2+Y/y0EOfFrPQPkuYphiWQYyu9+FzPKMB/pjuaLuKQ==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-link@3.31.3': + resolution: {integrity: sha512-986wOQzTL9Zr5lf84LCLpm+YOms8A0K39/8DVoqRfebqcOe0/eq4bnztmAlfabOd+kJY92g3AgZERFUx/w+dcw==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-list-item@3.31.3': + resolution: {integrity: sha512-4QlKOriJJMvg95QJTEsy9BUYPBQ6UvJyb8WURRwdUtQUkkqb8h32lg/eyQUv2FzW9IT1AdUyNZfVaxH64kRfQA==} + peerDependencies: + '@tiptap/extension-list': 3.31.3 + + '@tiptap/extension-list-keymap@3.31.3': + resolution: {integrity: sha512-If8UOEdDZbPJU6iYTvLtH6DOp2KBy6BKxg9UELL1AevVetGHEF/7lW8hP50Gn1tHMVpPRqmhSzVrJRpEJJgb/Q==} + peerDependencies: + '@tiptap/extension-list': 3.31.3 + + '@tiptap/extension-list@3.31.3': + resolution: {integrity: sha512-LoveGnC0FVdCV4jUNBaG1ZA+KWE07+adzV3kGy6uUYFcJEjbVUHTnPDrBOob3IwOSO3sCwIkvQb6EVYeXn/4yg==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-ordered-list@3.31.3': + resolution: {integrity: sha512-mp3g11NgA/PYu8rj7J7Ez3l4qBy6WfTSmHIG4PZvEGG5w2oUAIkgb9DU7nPPzjmeme27oazFYZw+AtZA0+u4tw==} + peerDependencies: + '@tiptap/extension-list': 3.31.3 + + '@tiptap/extension-paragraph@3.31.3': + resolution: {integrity: sha512-+iPku7wJfy5hbNDNLX8dveFtYVsZMmh7vztjuq8hT3mipSC4IByDehDbU8fzUCjfXiEzmI7mQn7c8LGmHULuzA==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-strike@3.31.3': + resolution: {integrity: sha512-G29bhKttYwcKHT+BI6emWVFol3RO/gUXxQVcmr/iT8LXXy7j8J6HFUnsKM+Kg5YlP1rxMRgsa65dbrQVahZi0A==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-text@3.31.3': + resolution: {integrity: sha512-gdsWtF+taeaCu6V+5Ct10fGo0ACUy1GnYtbb+mcathBt8OqbT+Ws60p/yEmKesBDz2Hn+B5IcWy6+2BBZl5ZTg==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-underline@3.31.3': + resolution: {integrity: sha512-HghdJaOwRqYzsAxqSyNyb+IWyOMcdCl8IoiBETA9BZCJAqdXzFLcuWp7CqCqJPam9dgkWosSoHqTCAO4nTBfpw==} + peerDependencies: + '@tiptap/core': 3.31.3 + + '@tiptap/extension-unique-id@3.31.3': + resolution: {integrity: sha512-ztX6wWurh8PLseQ+CfaX+QNKM6ARUSMxkGXUmQPWBiOwwRLGctGc4rhaFIaTsCyQlDl3tXA8Po8H2PQZ9AsqDQ==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/extensions@3.31.3': + resolution: {integrity: sha512-8sJNPGGUe8f3aDojcOW5cfVL7I5NrBbE0UWxG08qoi9Tea6qWbvQJsCR9tsrOapr/DaLr3kpbGZ9s1gEEfcNcA==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + + '@tiptap/pm@3.31.3': + resolution: {integrity: sha512-sZime0SWsz/k62W2WvHx5Ig7G2h7kVhrrmnqy+wEgIHfDwEfOlelRjaWCiBCFlF7dxGUntJusCh9FxlLhni0Ag==} + + '@tiptap/react@3.31.3': + resolution: {integrity: sha512-QiwQqvaLFLm5EMFu5tg7nAgXJxCUiUTLD8EsK+TqVV5P4bqOoMOCM39khbhXTJyahCuYpiFWx5YOSDtC/JiPtg==} + peerDependencies: + '@tiptap/core': 3.31.3 + '@tiptap/pm': 3.31.3 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tiptap/starter-kit@3.31.3': + resolution: {integrity: sha512-WKof9RewdmGHvWJ1wn0/HVNG2mV+HOgVRyJkKekuM9fgr6BZAAH/xZsWE1eon+94JnQ+KtK2ThydXQM/qc6b2A==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react@18.3.31': + resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@typescript-eslint/eslint-plugin@8.70.0': + resolution: {integrity: sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.70.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.70.0': + resolution: {integrity: sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.70.0': + resolution: {integrity: sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.70.0': + resolution: {integrity: sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.70.0': + resolution: {integrity: sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.70.0': + resolution: {integrity: sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.70.0': + resolution: {integrity: sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.70.0': + resolution: {integrity: sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.70.0': + resolution: {integrity: sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.70.0': + resolution: {integrity: sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} + + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} + + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.21: + resolution: {integrity: sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.18: + resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + + brace-expansion@2.1.4: + resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} + + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + + browserslist@4.28.9: + resolution: {integrity: sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + electron-to-chromium@1.5.425: + resolution: {integrity: sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-equals@5.4.2: + resolution: {integrity: sha512-Ywe6jodPTWOTL9/k0bV7gdfP8twKL5Y8I8CZ933fAY5gBekICZSUQTbyH6ut2NZCNyB05mSUwAuEqdEIaOOlDQ==} + engines: {node: '>=6.0.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.9: + resolution: {integrity: sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + linkifyjs@4.3.3: + resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.54: + resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} + engines: {node: '>=18'} + + nwsapi@2.2.27: + resolution: {integrity: sha512-gQPNF78qebCQ6tvVFBYrvJdBNOrYZm90ZlXgpIFm06p6qHDHq/XC4TnJftN6OMbxVE0UTBAoRgcsDeJBBooITw==} + + openapi-fetch@0.17.0: + resolution: {integrity: sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==} + + openapi-typescript-helpers@0.1.0: + resolution: {integrity: sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==} + + openapi-typescript@7.13.0: + resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==} + hasBin: true + peerDependencies: + typescript: ^5.x + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} + hasBin: true + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + prosemirror-changeset@2.4.2: + resolution: {integrity: sha512-ViYrjMSg3YFiXwIhKaluu+/mi3Yrxt6AR8ri14ulTaGcZtXO1CThl7A2gv79qx5fQnOw8woKwyBU2u+9PVCm3w==} + + prosemirror-commands@1.7.2: + resolution: {integrity: sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw==} + + prosemirror-dropcursor@1.8.3: + resolution: {integrity: sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ==} + + prosemirror-gapcursor@1.4.1: + resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==} + + prosemirror-history@1.5.0: + resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} + + prosemirror-inputrules@1.5.1: + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-model@1.25.11: + resolution: {integrity: sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-tables@1.8.5: + resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} + + prosemirror-transform@1.12.1: + resolution: {integrity: sha512-t4F5615FycnCqsX7ShTUs8+jfnwcf46kuFRvSl/3qFx2QTZ4DjgowesLHQXcFP7G//TjesqZG3WtgL7vdyVJyA==} + + prosemirror-view@1.42.3: + resolution: {integrity: sha512-oTN7EtH+CpwxU9NrwEYWd0UZ4JUx7l048l5A2Xppm4p/60isZYLnth9QVQmC3VRIvdrIWCxwZSd+Uz791G31/w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@7.18.3: + resolution: {integrity: sha512-ytVbyBBM7vMfRCam25r0WMhSVSom909A8p+8m0/f1w853dz/xfFu6etAT2SEbVoSnI+ZoPRDqIsQXVT89gp7kg==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.18.3: + resolution: {integrity: sha512-gyXgtdr5uACJ5b1Q4udzjVV+tb/rlHIMJKuJ0e89R4Kzgz47z/rgP0dIKxktqIEUhDHluGTPJJH/wRha7CyqsA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + rollup@4.63.1: + resolution: {integrity: sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript-eslint@8.70.0: + resolution: {integrity: sha512-P/W5cz70/cQAuKfY3xwQMWWTV7BvJ0mAQmi+9mBcsVPaBUpd6Ohpa+fECv9rBFrQcig86jAiNBFNWUqnTjr4pw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + update-browserslist-db@1.3.2: + resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js-replace@1.0.1: + resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + uuid@14.0.2: + resolution: {integrity: sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==} + hasBin: true + + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vite@6.4.3: + resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@10.2.2) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.9 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': + dependencies: + eslint: 9.39.5 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@10.2.2) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.7': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@10.2.2) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.2 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.15.0': {} + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + optional: true + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + optional: true + + '@floating-ui/utils@0.2.12': + optional: true + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@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.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@playwright/test@1.63.0': + dependencies: + playwright: 1.63.0 + + '@redocly/ajv@8.11.2': + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js-replace: 1.0.1 + + '@redocly/config@0.22.0': {} + + '@redocly/openapi-core@1.34.19(supports-color@10.2.2)': + dependencies: + '@redocly/ajv': 8.11.2 + '@redocly/config': 0.22.0 + colorette: 1.4.0 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + js-levenshtein: 1.1.6 + js-yaml: 4.3.1 + minimatch: 5.1.9 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - supports-color + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.63.1': + optional: true + + '@rollup/rollup-android-arm64@4.63.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.63.1': + optional: true + + '@rollup/rollup-darwin-x64@4.63.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.63.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.63.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.63.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.63.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.63.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.63.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.63.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.63.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.63.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.63.1': + optional: true + + '@tanstack/query-core@5.102.8': {} + + '@tanstack/react-query@5.102.8(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.102.8 + react: 18.3.1 + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/react@16.3.3(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.31 + '@types/react-dom': 18.3.7(@types/react@18.3.31) + + '@testing-library/user-event@14.6.7(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@tiptap/core@3.31.3(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-blockquote@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-bold@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-bubble-menu@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@floating-ui/dom': 1.8.0 + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + optional: true + + '@tiptap/extension-bullet-list@3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/extension-list': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + + '@tiptap/extension-code-block@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-code@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-document@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-dropcursor@3.31.3(@tiptap/extensions@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/extensions': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + + '@tiptap/extension-floating-menu@3.31.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@floating-ui/dom': 1.8.0 + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + optional: true + + '@tiptap/extension-gapcursor@3.31.3(@tiptap/extensions@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/extensions': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + + '@tiptap/extension-hard-break@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-heading@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-horizontal-rule@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-italic@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-link@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + linkifyjs: 4.3.3 + + '@tiptap/extension-list-item@3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/extension-list': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + + '@tiptap/extension-list-keymap@3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/extension-list': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + + '@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + + '@tiptap/extension-ordered-list@3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/extension-list': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + + '@tiptap/extension-paragraph@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-strike@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-text@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-underline@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + + '@tiptap/extension-unique-id@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + uuid: 14.0.2 + + '@tiptap/extensions@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + + '@tiptap/pm@3.31.3': + dependencies: + prosemirror-changeset: 2.4.2 + prosemirror-commands: 1.7.2 + prosemirror-dropcursor: 1.8.3 + prosemirror-gapcursor: 1.4.1 + prosemirror-history: 1.5.0 + prosemirror-inputrules: 1.5.1 + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.11 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.4 + prosemirror-tables: 1.8.5 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + '@tiptap/react@3.31.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + '@types/react': 18.3.31 + '@types/react-dom': 18.3.7(@types/react@18.3.31) + '@types/use-sync-external-store': 0.0.6 + fast-equals: 5.4.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) + optionalDependencies: + '@tiptap/extension-bubble-menu': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/extension-floating-menu': 3.31.3(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + transitivePeerDependencies: + - '@floating-ui/dom' + + '@tiptap/starter-kit@3.31.3': + dependencies: + '@tiptap/core': 3.31.3(@tiptap/pm@3.31.3) + '@tiptap/extension-blockquote': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/extension-bold': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-bullet-list': 3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)) + '@tiptap/extension-code': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-code-block': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/extension-document': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-dropcursor': 3.31.3(@tiptap/extensions@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)) + '@tiptap/extension-gapcursor': 3.31.3(@tiptap/extensions@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)) + '@tiptap/extension-hard-break': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-heading': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-horizontal-rule': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/extension-italic': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-link': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/extension-list': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/extension-list-item': 3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)) + '@tiptap/extension-list-keymap': 3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)) + '@tiptap/extension-ordered-list': 3.31.3(@tiptap/extension-list@3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3)) + '@tiptap/extension-paragraph': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-strike': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-text': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extension-underline': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3)) + '@tiptap/extensions': 3.31.3(@tiptap/core@3.31.3(@tiptap/pm@3.31.3))(@tiptap/pm@3.31.3) + '@tiptap/pm': 3.31.3 + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.8 + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.31)': + dependencies: + '@types/react': 18.3.31 + + '@types/react@18.3.31': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/use-sync-external-store@0.0.6': {} + + '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/type-utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.70.0 + eslint: 9.39.5 + ignore: 7.0.9 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.70.0 + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.70.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@5.9.3) + '@typescript-eslint/types': 8.70.0 + debug: 4.4.3(supports-color@10.2.2) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + + '@typescript-eslint/tsconfig-utils@8.70.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.70.0(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.5 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.70.0': {} + + '@typescript-eslint/typescript-estree@8.70.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.70.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@5.9.3) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + debug: 4.4.3(supports-color@10.2.2) + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.70.0(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + eslint-visitor-keys: 5.0.1 + + '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@22.20.1))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.3(@types/node@22.20.1) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@2.1.9': + dependencies: + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.20.1))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@22.20.1) + + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.9': + dependencies: + '@vitest/utils': 2.1.9 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.21 + pathe: 1.1.2 + + '@vitest/spy@2.1.9': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + loupe: 3.2.1 + tinyrainbow: 1.2.0 + + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + argparse@2.0.1: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + assertion-error@2.0.1: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.11.21: {} + + brace-expansion@1.1.18: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.1.4: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.9: + dependencies: + baseline-browser-mapping: 2.11.21 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.425 + node-releases: 2.0.54 + update-browserslist-db: 1.3.2(browserslist@4.28.9) + + cac@6.7.14: {} + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001810: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + change-case@5.4.4: {} + + check-error@2.1.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@1.4.0: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + csstype@3.2.3: {} + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + debug@4.4.3(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + decimal.js@10.6.0: {} + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + dequal@2.0.3: {} + + dom-accessibility-api@0.5.16: {} + + electron-to-chromium@1.5.425: {} + + entities@6.0.1: {} + + es-module-lexer@1.7.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@5.2.0(eslint@9.39.5): + dependencies: + eslint: 9.39.5 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5: + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.7 + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@10.2.2) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + expect-type@1.4.0: {} + + fast-deep-equal@3.1.3: {} + + fast-equals@5.4.2: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + + flatted@3.4.4: {} + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + has-flag@4.0.0: {} + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6(supports-color@10.2.2): + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.9: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + index-to-position@1.2.0: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-potential-custom-element-name@1.0.1: {} + + isexe@2.0.0: {} + + js-levenshtein@1.1.6: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + jsdom@26.1.0: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6(supports-color@10.2.2) + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.27 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.21.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + linkifyjs@4.3.3: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.18 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.4 + + ms@2.1.3: {} + + nanoid@3.3.18: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.54: {} + + nwsapi@2.2.27: {} + + openapi-fetch@0.17.0: + dependencies: + openapi-typescript-helpers: 0.1.0 + + openapi-typescript-helpers@0.1.0: {} + + openapi-typescript@7.13.0(typescript@5.9.3): + dependencies: + '@redocly/openapi-core': 1.34.19(supports-color@10.2.2) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.3.0 + supports-color: 10.2.2 + typescript: 5.9.3 + yargs-parser: 21.1.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + orderedmap@2.1.1: {} + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.29.7 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + pathe@1.1.2: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + playwright-core@1.63.0: {} + + playwright@1.63.0: + dependencies: + playwright-core: 1.63.0 + + pluralize@8.0.0: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@3.9.6: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + prosemirror-changeset@2.4.2: + dependencies: + prosemirror-transform: 1.12.1 + + prosemirror-commands@1.7.2: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + prosemirror-dropcursor@1.8.3: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + prosemirror-gapcursor@1.4.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-view: 1.42.3 + + prosemirror-history@1.5.0: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-model@1.25.11: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + prosemirror-tables@1.8.5: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + prosemirror-transform@1.12.1: + dependencies: + prosemirror-model: 1.25.11 + + prosemirror-view@1.42.3: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + punycode@2.3.1: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@17.0.2: {} + + react-refresh@0.17.0: {} + + react-router-dom@7.18.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.18.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-router@7.18.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.1.1 + react: 18.3.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + rollup@4.63.1: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.63.1 + '@rollup/rollup-android-arm64': 4.63.1 + '@rollup/rollup-darwin-arm64': 4.63.1 + '@rollup/rollup-darwin-x64': 4.63.1 + '@rollup/rollup-freebsd-arm64': 4.63.1 + '@rollup/rollup-freebsd-x64': 4.63.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.1 + '@rollup/rollup-linux-arm-musleabihf': 4.63.1 + '@rollup/rollup-linux-arm64-gnu': 4.63.1 + '@rollup/rollup-linux-arm64-musl': 4.63.1 + '@rollup/rollup-linux-loong64-gnu': 4.63.1 + '@rollup/rollup-linux-loong64-musl': 4.63.1 + '@rollup/rollup-linux-ppc64-gnu': 4.63.1 + '@rollup/rollup-linux-ppc64-musl': 4.63.1 + '@rollup/rollup-linux-riscv64-gnu': 4.63.1 + '@rollup/rollup-linux-riscv64-musl': 4.63.1 + '@rollup/rollup-linux-s390x-gnu': 4.63.1 + '@rollup/rollup-linux-x64-gnu': 4.63.1 + '@rollup/rollup-linux-x64-musl': 4.63.1 + '@rollup/rollup-openbsd-x64': 4.63.1 + '@rollup/rollup-openharmony-arm64': 4.63.1 + '@rollup/rollup-win32-arm64-msvc': 4.63.1 + '@rollup/rollup-win32-ia32-msvc': 4.63.1 + '@rollup/rollup-win32-x64-gnu': 4.63.1 + '@rollup/rollup-win32-x64-msvc': 4.63.1 + fsevents: 2.3.3 + + rope-sequence@1.3.4: {} + + rrweb-cssom@0.8.0: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.8.5: {} + + set-cookie-parser@2.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@10.2.2: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tinypool@1.1.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@4.41.0: {} + + typescript-eslint@8.70.0(eslint@9.39.5)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + update-browserslist-db@1.3.2(browserslist@4.28.9): + dependencies: + browserslist: 4.28.9 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js-replace@1.0.1: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.6.0(react@18.3.1): + dependencies: + react: 18.3.1 + + uuid@14.0.2: {} + + vite-node@2.1.9(@types/node@22.20.1): + dependencies: + cac: 6.7.14 + debug: 4.4.3(supports-color@10.2.2) + es-module-lexer: 1.7.0 + pathe: 1.1.2 + vite: 5.4.21(@types/node@22.20.1) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.21(@types/node@22.20.1): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.28 + rollup: 4.63.1 + optionalDependencies: + '@types/node': 22.20.1 + fsevents: 2.3.3 + + vite@6.4.3(@types/node@22.20.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + postcss: 8.5.28 + rollup: 4.63.1 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.1 + fsevents: 2.3.3 + + vitest@2.1.9(@types/node@22.20.1)(jsdom@26.1.0): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.20.1)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.3.3 + debug: 4.4.3(supports-color@10.2.2) + expect-type: 1.4.0 + magic-string: 0.30.21 + pathe: 1.1.2 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.1.1 + tinyrainbow: 1.2.0 + vite: 5.4.21(@types/node@22.20.1) + vite-node: 2.1.9(@types/node@22.20.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.20.1 + jsdom: 26.1.0 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + w3c-keyname@2.2.8: {} + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + ws@8.21.3: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + yallist@3.1.1: {} + + yaml-ast-parser@0.0.43: {} + + yargs-parser@21.1.1: {} + + yocto-queue@0.1.0: {} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..86e559e --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src", "tests"] +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..0e5f1b3 --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,20 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +// 作者工作台构建配置:开发代理指向本地后端(W07 起接入真实路由)。 +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + proxy: { + "/api": { + target: "http://127.0.0.1:8080", + changeOrigin: true, + }, + }, + }, + build: { + outDir: "dist", + emptyOutDir: true, + }, +}); diff --git a/web/vitest.config.ts b/web/vitest.config.ts new file mode 100644 index 0000000..3d080c1 --- /dev/null +++ b/web/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; + +// 组件与单元检查:只证明组件范围;真实浏览器旅程由 Playwright 配置承担。 +export default defineConfig({ + test: { + environment: "jsdom", + setupFiles: ["tests/设置.ts"], + include: ["tests/**/*.test.{ts,tsx}", "src/**/*.test.{ts,tsx}"], + // 无用例时如实失败(不冒充通过) + passWithNoTests: false, + }, +}); diff --git a/工具/构建资源包.py b/工具/构建资源包.py new file mode 100644 index 0000000..af59d55 --- /dev/null +++ b/工具/构建资源包.py @@ -0,0 +1,185 @@ +"""生成运行资源与发布清单。 + +工程阶段的生成或预检入口: +- 从唯一登记输入(工具/资源登记.json)打包 src/muse/资源/运行资源.zip; +- 写出 src/muse/资源/清单.json(发布身份 + 每个资源的相对路径与 sha256); +- 投影 web/dist → src/muse/资源/工作台/(web 构建存在时)。 + +边界:只打包已登记生产资源;不读取未审工作树内容、不打包 tests/.git/当前目录; +生成物幂等可重现(固定排序、固定压缩时间戳)。 +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import shutil +import tomllib +import zipfile +from pathlib import Path +from typing import Any + +from muse.资源加载 import 解析能力声明 + +仓库根 = Path(__file__).resolve().parent.parent +登记文件 = 仓库根 / "工具" / "资源登记.json" +资源目录 = 仓库根 / "src" / "muse" / "资源" +压缩包名 = "运行资源.zip" +清单名 = "清单.json" +_固定时间戳 = (2026, 1, 1, 0, 0, 0) + + +def 读取登记() -> dict[str, Any]: + if not 登记文件.is_file(): + raise SystemExit(f"资源登记不存在:{登记文件}(打包前必须先登记生产资源)") + 登记: Any = json.loads(登记文件.read_text(encoding="utf-8")) + if not isinstance(登记, dict) or "资源" not in 登记: + raise SystemExit("资源登记不合法:缺少 资源 表") + return 登记 + + +def _sha256(文件: Path) -> str: + return hashlib.sha256(文件.read_bytes()).hexdigest() + + +def 编译能力资源( + 根: Path, 文件清单: list[str], 已实现命令: set[str] +) -> tuple[dict, dict[str, bytes]]: + 目录: dict[str, dict] = {"role": {}, "method": {}, "operation": {}} + 资源: dict[str, bytes] = {} + for 相对 in sorted(文件清单): + 文件 = (根 / 相对).resolve() + if not 文件.is_relative_to(根.resolve()) or not 文件.is_file(): + raise ValueError("能力资源必须是登记根内的实际文件") + 数据 = 文件.read_bytes() + 声明, _ = 解析能力声明(数据) + 分类, 身份 = 声明["category"], 声明["id"] + if 身份 in 目录[分类]: + raise ValueError("能力稳定身份重复") + 命令 = 声明.get("commands", []) + if not isinstance(命令, list) or any( + not isinstance(v, str) or v not in 已实现命令 for v in 命令 + ): + raise ValueError("能力声明包含未实现命令") + if 分类 == "operation" and not 命令: + raise ValueError("操作技能必须声明已实现命令") + 路径 = f"能力/{分类}/{身份}.md" + 资源[路径] = 数据 + 目录[分类][身份] = { + "id": 身份, + "name": 声明["name"], + "description": 声明["description"], + "contract_version": 声明["contract_version"], + "commands": 命令, + "resource_path": 路径, + "sha256": hashlib.sha256(数据).hexdigest(), + } + return 目录, 资源 + + +def 打包() -> dict[str, Any]: + 登记 = 读取登记() + 发布身份 = str(登记.get("发布身份", "")) + if not 发布身份.strip(): + raise SystemExit("资源登记缺少 发布身份") + 条目: list[dict[str, str]] = 登记["资源"] + 能力资源: dict[str, bytes] = {} + if 登记.get("能力文件"): + from muse.接入.http.应用 import 导出合同 + + 合同 = 导出合同() + 命令 = { + 值["operationId"] + for 路径 in 合同["paths"].values() + for 值 in 路径.values() + if "operationId" in 值 + } + 目录, 能力资源 = 编译能力资源(仓库根, 登记["能力文件"], 命令) + 能力资源["能力/目录.json"] = json.dumps( + 目录, ensure_ascii=False, sort_keys=True, indent=2 + ).encode() + 资源目录.mkdir(parents=True, exist_ok=True) + 压缩包路径 = 资源目录 / 压缩包名 + + 已打包: dict[str, str] = {} + with zipfile.ZipFile(压缩包路径, "w", compression=zipfile.ZIP_DEFLATED) as zf: + for 条 in sorted(条目, key=lambda x: str(x.get("打包路径", ""))): + 源 = 仓库根 / str(条["源路径"]) + 打包路径 = str(条["打包路径"]) + if not 源.is_file(): + raise SystemExit(f"登记资源不存在:{源}") + 信息 = zipfile.ZipInfo(打包路径, date_time=_固定时间戳) + 信息.compress_type = zipfile.ZIP_DEFLATED + 信息.external_attr = 0o644 << 16 + zf.writestr(信息, 源.read_bytes()) + 已打包[打包路径] = _sha256(源) + for 路径, 数据 in sorted(能力资源.items()): + if 路径 in 已打包: + raise ValueError("生成能力资源与直接登记路径重复") + 信息 = zipfile.ZipInfo(路径, date_time=_固定时间戳) + 信息.compress_type = zipfile.ZIP_DEFLATED + 信息.external_attr = 0o644 << 16 + zf.writestr(信息, 数据) + 已打包[路径] = hashlib.sha256(数据).hexdigest() + + # 可编辑安装和正式 wheel 使用相同的包内迁移入口。 + 迁移源 = 登记.get("迁移目录") + if 迁移源: + 源目录 = 仓库根 / 迁移源 + if not 源目录.is_dir(): + raise SystemExit("登记的迁移目录不存在") + 迁移资源 = 资源目录 / "迁移" + if 迁移资源.exists(): + shutil.rmtree(迁移资源) + 迁移资源.mkdir() + for 文件 in sorted(源目录.glob("V*.sql")): + shutil.copyfile(文件, 迁移资源 / 文件.name) + 已打包[f"迁移/{文件.name}"] = _sha256(文件) + + # 工作台投影:web 构建产物存在时带上;不存在时如实留空。 + 工作台目录 = 资源目录 / "工作台" + if 工作台目录.exists(): + shutil.rmtree(工作台目录) + web_dist = 仓库根 / "web" / "dist" + if web_dist.is_dir(): + shutil.copytree(web_dist, 工作台目录) + for 文件 in sorted(工作台目录.rglob("*")): + if 文件.is_file(): + 相对 = 文件.relative_to(资源目录).as_posix() + 已打包[相对] = _sha256(文件) + + 清单 = { + "schema_version": 1, + "发布身份": 发布身份, + "资源": {路径: {"sha256": 哈希} for 路径, 哈希 in sorted(已打包.items())}, + } + 代码文件 = { + 文件.relative_to(仓库根).as_posix(): _sha256(文件) + for 文件 in (仓库根 / "src/muse").rglob("*") + if 文件.suffix in {".py", ".ts"} + and "资源" not in 文件.relative_to(仓库根 / "src/muse").parts + } + 代码哈希 = hashlib.sha256(json.dumps(代码文件, sort_keys=True).encode()).hexdigest() + with (仓库根 / "pyproject.toml").open("rb") as 文件: + 包版本 = tomllib.load(文件)["project"]["version"] + 清单.update({"代码版本": 包版本, "代码哈希": 代码哈希, "合同版本": 1}) + 清单["构建身份"] = hashlib.sha256( + json.dumps(清单, ensure_ascii=False, sort_keys=True).encode() + ).hexdigest() + (资源目录 / 清单名).write_text( + json.dumps(清单, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" + ) + return 清单 + + +def 主() -> int: + 解析器 = argparse.ArgumentParser(description="生成运行资源与发布清单") + 解析器.parse_args() + 清单 = 打包() + print(f"构建资源包:发布身份 {清单['发布身份']},资源 {len(清单['资源'])} 项") + return 0 + + +if __name__ == "__main__": + raise SystemExit(主()) diff --git a/工具/生成接口.py b/工具/生成接口.py new file mode 100644 index 0000000..d5e2ffb --- /dev/null +++ b/工具/生成接口.py @@ -0,0 +1,64 @@ +"""从登记的实际路由工厂生成 OpenAPI;不启动应用、不连接数据库或读取凭据。 + +未支持的非空输入失败,生成结果由同一公开合同产生。 +""" + +from __future__ import annotations + +import argparse +import importlib +import json +from pathlib import Path +from typing import Any + +仓库根 = Path(__file__).resolve().parent.parent +登记文件 = 仓库根 / "工具" / "生成输入.json" + + +def 已登记输入() -> dict[str, Any]: + """读取生成登记;文件不存在视为空登记(如实报告,不猜测)。""" + if not 登记文件.is_file(): + return {"资源": []} + 数据: Any = json.loads(登记文件.read_text(encoding="utf-8")) + if not isinstance(数据, dict) or not isinstance(数据.get("资源"), list): + raise SystemExit(f"生成输入登记不合法:{登记文件}(需要 dict 含 资源 表)") + return 数据 + + +def 主() -> int: + 解析器 = argparse.ArgumentParser(description="生成 Schema、OpenAPI 和 TypeScript") + 解析器.parse_args() + + 输入 = 已登记输入() + if not 输入["资源"]: + print("生成接口:无登记输入,未生成任何合同(业务外壳随 W04/W07 定稿后登记)") + return 0 + for 条目 in 输入["资源"]: + if 条目.get("格式") != "OpenAPI" or not 条目.get("工厂") or not 条目.get("输出"): + raise SystemExit("生成接口:未接入此输入的生成器,未生成合同") + 模块, 名称 = 条目["工厂"].split(":", 1) + 合同 = getattr(importlib.import_module(模块), 名称)() + if not isinstance(合同, dict) or "openapi" not in 合同: + raise SystemExit("生成接口:工厂未返回 OpenAPI 合同") + 输出 = 仓库根 / 条目["输出"] + 输出.parent.mkdir(parents=True, exist_ok=True) + 输出.write_text(json.dumps(合同, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + 客户端 = 仓库根 / "web/src/接口/生成/客户端.ts" + 客户端.parent.mkdir(parents=True, exist_ok=True) + 客户端.write_text( + "// 由工具/生成接口.py生成;稳定外壳来自同一OpenAPI。\n" + 'import createClient from "openapi-fetch";\n' + 'import type { paths } from "./类型";\n' + 'import { 传输 } from "../传输";\n\n' + "export const 客户端 = createClient({\n" + " baseUrl: globalThis.location.origin, fetch: 传输,\n" + ' credentials: "same-origin"\n' + "});\n", + encoding="utf-8", + ) + print(f"生成接口:{条目['输出']}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(主()) diff --git a/工具/生成输入.json b/工具/生成输入.json new file mode 100644 index 0000000..443b590 --- /dev/null +++ b/工具/生成输入.json @@ -0,0 +1,11 @@ +{ + "schema_version": 1, + "说明": "接口生成器的唯一登记输入。每个条目声明稳定业务外壳的位置与生成目标;空登记时生成器不产出任何合同。业务外壳随 W04(元数据投影)与 W07(HTTP 合同)定稿后登记。", + "资源": [ + { + "格式": "OpenAPI", + "工厂": "muse.接入.http.应用:导出合同", + "输出": "docs/接口契约/生成/openapi.generated.json" + } + ] +} diff --git a/工具/维护索引.py b/工具/维护索引.py new file mode 100644 index 0000000..c924f3a --- /dev/null +++ b/工具/维护索引.py @@ -0,0 +1,279 @@ +"""索引生成、查重及检查。 + +工程阶段的生成或预检入口: +- 核对 Markdown 索引(目录.md)表内相对链接有效; +- 核对技能目录(.agent/skills/ 下分组)中每个技能有 SKILL.md,名称不重复; +- 核对测试符号与用例清单的 case 身份一致:仓库内每个 test_ 符号必须带 + ``__`` 后缀且能在清单定位;清单登记的目标符号必须在树上真实存在; + 只有 print 没有 assert/raise 的用例在执行前即被标记(反假绿)。 + +边界:只报告差异与失败,不改变业务数据、不自行批准发布。 +生成物与唯一输入一致;命令可独立重跑。所有检查函数接受显式根目录, +供测试在临时项目上验证同一语义。 +""" + +from __future__ import annotations + +import argparse +import ast +import json +import re +import sys +import unicodedata +from pathlib import Path +from typing import Any + +仓库根 = Path(__file__).resolve().parent.parent +_目录表行 = re.compile(r"^\|\s*([^|]+)\|\s*\[([^\]]+)\]\(([^)]+)\)\s*\|") +_用例后缀 = re.compile(r"__([0-9a-f]{6})$") +_忽略目录片段 = frozenset( + {".venv", ".venv-旧", ".git", "node_modules", "__pycache__", ".agents.local", "研究依据"} +) +# 新版用例目录:旧测试树(tests/skills、tests/adapters、tests/architecture 英文目录等) +# 由旧环境运行,不进入新版用例身份检查 +_新版用例目录 = ("单元", "契约", "集成", "架构") + + +def _在忽略目录(路径: Path, 根: Path) -> bool: + try: + 相对 = 路径.relative_to(根) + except ValueError: + return True + return any(片段 in 相对.parts for 片段 in _忽略目录片段) + + +def 检查目录链接(根: Path) -> list[str]: + """核对仓库内 目录.md 的表格相对链接;返回问题列表。""" + 问题: list[str] = [] + for 目录文件 in 根.rglob("目录.md"): + if _在忽略目录(目录文件, 根): + continue + for 行号, 行 in enumerate(目录文件.read_text(encoding="utf-8").splitlines(), 1): + 匹配 = _目录表行.match(行) + if not 匹配: + continue + 地址 = 匹配.group(3).split("#")[0].strip() + if not 地址 or 地址.startswith(("http://", "https://")): + continue + 目标 = (目录文件.parent / 地址).resolve() + if not 目标.exists(): + 问题.append(f"{目录文件.relative_to(根)}:{行号} 链接不存在:{地址}") + return 问题 + + +def _读frontmatter名(文件: Path) -> str | None: + """读 SKILL.md frontmatter 的 name 字段;无 frontmatter 返回 None。""" + 文本 = 文件.read_text(encoding="utf-8") + if not 文本.startswith("---"): + return None + 结束 = 文本.find("\n---", 3) + if 结束 < 0: + return None + for 行 in 文本[3:结束].splitlines(): + if 行.startswith("name:"): + return 行[5:].strip() + return None + + +def _含中文(名称: str) -> bool: + return any("\u4e00" <= ch <= "\u9fff" for ch in 名称) + + +def 检查技能目录(根: Path) -> list[str]: + """核对 .agent/skills 分组:每个技能有非空 SKILL.md、名称不重复、 + frontmatter name 与目录一致、目录名全中文且规范化(NFC、无空白)。""" + 技能根 = 根 / ".agent" / "skills" + if not 技能根.is_dir(): + return ["技能目录不存在:.agent/skills(缺失即失败,不静默跳过)"] + 问题: list[str] = [] + 已见: dict[str, str] = {} + for 分组 in sorted(p for p in 技能根.iterdir() if p.is_dir()): + for 技能目录 in sorted(p for p in 分组.iterdir() if p.is_dir()): + 名称 = 技能目录.name + 相对 = str(技能目录.relative_to(根)) + 技能文件 = 技能目录 / "SKILL.md" + if not 技能文件.is_file(): + 问题.append(f"技能缺少 SKILL.md:{相对}") + continue + 正文 = 技能文件.read_text(encoding="utf-8").strip() + if not 正文 or 正文 == "---": + 问题.append(f"SKILL.md 为空:{相对}") + continue + front名 = _读frontmatter名(技能文件) + if front名 is None: + 问题.append(f"SKILL.md 缺少 frontmatter name:{相对}") + elif front名 != 名称: + 问题.append(f"frontmatter name 与目录不一致:{相对}(name: {front名})") + if not _含中文(名称): + 问题.append(f"技能名称必须全中文(政策 chinese_only):{相对}") + if 名称 != unicodedata.normalize("NFC", 名称): + 问题.append(f"技能名称不是 NFC 规范形式:{相对}") + if any(ch.isspace() for ch in 名称): + 问题.append(f"技能名称含空白:{相对}") + if 名称 in 已见: + 问题.append(f"技能名称重复:{名称}({已见[名称]} 与 {相对})") + else: + 已见[名称] = 相对 + return 问题 + + +def 解析测试符号(文件: Path) -> list[ast.FunctionDef]: + """用 AST 提取 test_ 开头的函数与方法;不执行任何代码。""" + 树 = ast.parse(文件.read_text(encoding="utf-8")) + return [ + 节点 + for 节点 in ast.walk(树) + if isinstance(节点, ast.FunctionDef) and 节点.name.startswith("test_") + ] + + +def _有判定语句(节点: ast.FunctionDef) -> bool: + """函数体内是否有 assert、raise 或 pytest.fail/skip/xfail 调用。""" + for 子 in ast.walk(节点): + if isinstance(子, ast.Assert): + return True + if isinstance(子, ast.Raise): + return True + if isinstance(子, ast.Call) and isinstance(子.func, ast.Attribute): + if 子.func.attr in {"fail", "skip", "xfail", "raises"}: + return True + return False + + +def 检查用例身份( + 根: Path, + 清单路径: Path | None = None, + *, + 核对缺失目标: bool = False, + 全树扫描: bool = False, +) -> list[str]: + """核对测试符号与用例清单双向一致: + 1. 新版用例目录内符号按完整文件、函数或前端用例名绑定清单; + 2. 用例体必须包含可判定语句(assert/raise/fail/skip/xfail); + 3. 核对缺失目标=True 时,清单登记的 (file, symbol) 必须在树上真实存在 + 已实施条目始终反向核对;过渡期只容许未实施目标暂缺。 + """ + if 清单路径 is None: + 清单路径 = 根 / "docs" / "系统架构" / "新版设计" / "验证设计" / "测试用例清单.json" + 问题: list[str] = [] + if not 清单路径.is_file(): + return [f"用例清单不存在:{清单路径}"] + 清单: dict[str, Any] = json.loads(清单路径.read_text(encoding="utf-8")) + + 登记符号: dict[str, str] = {} + 已实施目标: set[str] = set() + for 用例 in 清单.get("cases", []): + for 目标 in 用例.get("target_cases") or []: + case_id = str(目标.get("case_id", "")) + 文件 = str(目标.get("file", "")) + 符号 = str(目标.get("symbol", "")) + if 文件 and 符号: + 登记符号[f"{文件}::{符号}"] = case_id + if 目标.get("verification_state") in {"通过", "已实现未执行", "失败"}: + 已实施目标.add(f"{文件}::{符号}") + for 新例 in 清单.get("new_cases", []): + case_id = str(新例.get("case_id", "")) + 符号 = str(新例.get("symbol", "")) + 文件 = str(新例.get("file", "")) + if 文件 and 符号: + 登记符号[f"{文件}::{符号}"] = case_id + if 新例.get("verification_state") in {"通过", "已实现未执行", "失败"}: + 已实施目标.add(f"{文件}::{符号}") + + 树上符号: set[str] = set() + 候选 = sorted(根.glob("**/test_*.py")) if 全树扫描 else sorted(根.glob("tests/**/test_*.py")) + for 测试文件 in 候选: + if _在忽略目录(测试文件, 根): + continue + if not 全树扫描: + # 只检查新版用例目录;旧测试树由旧环境运行 + try: + 相对路径 = 测试文件.relative_to(根 / "tests") + except ValueError: + continue + if not 相对路径.parts or 相对路径.parts[0] not in _新版用例目录: + continue + try: + 相对 = 测试文件.relative_to(根).as_posix() + except ValueError: + continue + for 节点 in 解析测试符号(测试文件): + 树上符号.add(f"{相对}::{节点.name}") + 匹配 = _用例后缀.search(节点.name) + if not 匹配: + 问题.append(f"测试符号缺少用例后缀:{相对}::{节点.name}") + if f"{相对}::{节点.name}" not in 登记符号: + 问题.append(f"测试文件与符号未在用例清单登记:{相对}::{节点.name}") + if not _有判定语句(节点): + 问题.append(f"用例无可判定语句(print 不能代替断言):{相对}::{节点.name}") + + 前端测试 = sorted( + [*(根 / "web/tests").rglob("*.test.ts*"), *(根 / "web/tests").rglob("*.spec.ts*")] + ) + if 前端测试: + import subprocess + + # TypeScript AST 只解析静态用例声明,不执行测试或加载其运行依赖。 + 脚本 = """ +import ts from 'typescript'; +import fs from 'node:fs'; +const files = JSON.parse(fs.readFileSync(0, 'utf8')); +const found = []; +for (const file of files) { + const source = ts.createSourceFile(file, fs.readFileSync(file, 'utf8'), + ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); + function visit(node) { + if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) + && ['it', 'test'].includes(node.expression.text) + && node.arguments[0] && ts.isStringLiteral(node.arguments[0])) + found.push([file, node.arguments[0].text]); + ts.forEachChild(node, visit); + } + visit(source); +} +process.stdout.write(JSON.stringify(found)); +""" + try: + 结果 = subprocess.run( + ["node", "--input-type=module", "-e", 脚本], + cwd=根 / "web", + input=json.dumps([str(p) for p in 前端测试]), + capture_output=True, + text=True, + timeout=20, + check=True, + ) + for 文件, 符号 in json.loads(结果.stdout): + 键 = f"{Path(文件).relative_to(根).as_posix()}::{符号}" + 树上符号.add(键) + if 键 not in 登记符号: + 问题.append(f"前端用例未在清单登记:{键}") + except (OSError, ValueError, subprocess.SubprocessError): + 问题.append("前端用例解析失败;需要已锁定的 Node 与 TypeScript 工具链") + + for 键, case_id in sorted(登记符号.items()): + if 核对缺失目标 or 键 in 已实施目标: + if 键 not in 树上符号: + 问题.append(f"用例清单登记的目标不存在:{键}({case_id})") + return 问题 + + +def 主() -> int: + 解析器 = argparse.ArgumentParser(description="索引生成、查重及检查") + 解析器.add_argument("--写入", action="store_true", help="执行索引写入(当前为幂等检查)") + 解析器.add_argument("--检查", action="store_true", help="只检查并报告差异") + 参数 = 解析器.parse_args() + if 参数.写入: + print("索引写入:当前无可写生成物,执行幂等检查") + + 问题 = 检查目录链接(仓库根) + 检查技能目录(仓库根) + 检查用例身份(仓库根) + if 问题: + for 条 in 问题: + print(f"索引问题:{条}", file=sys.stderr) + return 1 + print("索引检查通过:目录链接、技能目录与用例身份均一致") + return 0 + + +if __name__ == "__main__": + raise SystemExit(主()) diff --git a/工具/资源登记.json b/工具/资源登记.json new file mode 100644 index 0000000..13d102d --- /dev/null +++ b/工具/资源登记.json @@ -0,0 +1,28 @@ +{ + "schema_version": 1, + "说明": "运行资源包的唯一登记输入。每个条目:源路径(相对仓库根)与打包路径(zip 内相对路径,工作台文件用 工作台/ 前缀之外的稳定路径)。生产资源随 W04/W06/W13/W20/W24 逐包登记;本文件是唯一输入,运行资源.zip 与 清单.json 只由它产生。", + "发布身份": "muse-foundation-r2", + "资源": [ + { + "源路径": "配置/角色策略.yaml", + "打包路径": "策略/角色.yaml" + }, + { + "源路径": ".agent/skills/操作/完善故事基础设定/references/交付结构.json", + "打包路径": "规划/探索交付结构.json" + } + ], + "迁移目录": "数据库/迁移", + "能力文件": [ + ".agent/角色/写手.md", + ".agent/角色/规划员.md", + ".agent/角色/知识抽取员.md", + ".agent/角色/检测员.md", + ".agent/角色/评委.md", + ".agent/skills/操作/维护元数据结构/SKILL.md", + ".agent/skills/操作/查看与恢复任务/SKILL.md", + ".agent/skills/操作/决定正文候选去留/SKILL.md", + ".agent/skills/操作/完善故事基础设定/SKILL.md", + ".agent/skills/操作/确认规划候选/SKILL.md" + ] +} diff --git a/配置/测试.example.toml b/配置/测试.example.toml new file mode 100644 index 0000000..7dad3a9 --- /dev/null +++ b/配置/测试.example.toml @@ -0,0 +1,14 @@ +# 隔离测试与真实调用的显式配置模板(不含凭据值)。 +# 复制为 配置/测试.toml 或按环境变量注入;缺配置必须失败,不回退示例值。 + +["测试"."数据库"] +# 隔离 PostgreSQL 连接串的环境变量名(值不入仓、不入文档) +"连接环境变量" = "MUSE_TEST_DATABASE_URL" + +["测试"."隔离"] +# 用途隔离:测试进程只允许 evaluation / maintenance 用途 +"允许用途" = ["evaluation", "maintenance"] + +["测试"."模型"] +# 真实模型调用默认关闭;需要时显式授权并记录预算 +"真实调用" = false