Bläddra i källkod

第二版播报模板管理系统

sequoiaoo 1 månad sedan
incheckning
19ce759ee1
100 ändrade filer med 14015 tillägg och 0 borttagningar
  1. 221 0
      FEATURES.md
  2. 61 0
      README.md
  3. 140 0
      README_DEPLOY.md
  4. 203 0
      SYSTEM_GUIDE.md
  5. BIN
      __pycache__/main.cpython-311.pyc
  6. 0 0
      backend/app/__init__.py
  7. BIN
      backend/app/__pycache__/__init__.cpython-311.pyc
  8. BIN
      backend/app/__pycache__/main.cpython-311.pyc
  9. BIN
      backend/app/__pycache__/schemas.cpython-311.pyc
  10. 430 0
      backend/app/config/machine_config.py
  11. 165 0
      backend/app/main.py
  12. 117 0
      backend/app/schemas.py
  13. BIN
      backend/app/services/__pycache__/machine_store.cpython-311.pyc
  14. BIN
      backend/app/services/__pycache__/renderer.cpython-311.pyc
  15. BIN
      backend/app/services/__pycache__/template_store.cpython-311.pyc
  16. 71 0
      backend/app/services/machine_store.py
  17. 546 0
      backend/app/services/renderer.py
  18. 179 0
      backend/app/services/template_store.py
  19. 64 0
      backend/data/machine_configs.json
  20. 6 0
      backend/data/reuse_map.json
  21. 26 0
      backend/data/templates.json
  22. 5 0
      backend/requirements.txt
  23. 17 0
      check_api.py
  24. 9 0
      check_api_data.py
  25. 50 0
      check_real_template.py
  26. 15 0
      check_templates.py
  27. 30 0
      check_templates_body.py
  28. 24 0
      fix_season_random.py
  29. 18 0
      fix_templates.py
  30. 24 0
      frontend/.gitignore
  31. 3 0
      frontend/.vscode/extensions.json
  32. 5 0
      frontend/README.md
  33. 13 0
      frontend/index.html
  34. 1150 0
      frontend/package-lock.json
  35. 18 0
      frontend/package.json
  36. 1 0
      frontend/public/vite.svg
  37. 1749 0
      frontend/src/App.vue
  38. 1 0
      frontend/src/assets/vue.svg
  39. 43 0
      frontend/src/components/HelloWorld.vue
  40. 5 0
      frontend/src/main.js
  41. 103 0
      frontend/src/services/api.js
  42. 97 0
      frontend/src/style.css
  43. 11 0
      frontend/vite.config.js
  44. 7 0
      import_output.txt
  45. 3 0
      main.py
  46. 12 0
      node_modules/.bin/esbuild
  47. 17 0
      node_modules/.bin/esbuild.cmd
  48. 28 0
      node_modules/.bin/esbuild.ps1
  49. 12 0
      node_modules/.bin/nanoid
  50. 17 0
      node_modules/.bin/nanoid.cmd
  51. 28 0
      node_modules/.bin/nanoid.ps1
  52. 12 0
      node_modules/.bin/parser
  53. 17 0
      node_modules/.bin/parser.cmd
  54. 28 0
      node_modules/.bin/parser.ps1
  55. 12 0
      node_modules/.bin/rollup
  56. 17 0
      node_modules/.bin/rollup.cmd
  57. 28 0
      node_modules/.bin/rollup.ps1
  58. 12 0
      node_modules/.bin/vite
  59. 17 0
      node_modules/.bin/vite.cmd
  60. 28 0
      node_modules/.bin/vite.ps1
  61. 381 0
      node_modules/.package-lock.json
  62. 22 0
      node_modules/@babel/helper-string-parser/LICENSE
  63. 19 0
      node_modules/@babel/helper-string-parser/README.md
  64. 295 0
      node_modules/@babel/helper-string-parser/lib/index.js
  65. 0 0
      node_modules/@babel/helper-string-parser/lib/index.js.map
  66. 31 0
      node_modules/@babel/helper-string-parser/package.json
  67. 22 0
      node_modules/@babel/helper-validator-identifier/LICENSE
  68. 19 0
      node_modules/@babel/helper-validator-identifier/README.md
  69. 8 0
      node_modules/@babel/helper-validator-identifier/lib/identifier.js
  70. 0 0
      node_modules/@babel/helper-validator-identifier/lib/identifier.js.map
  71. 57 0
      node_modules/@babel/helper-validator-identifier/lib/index.js
  72. 1 0
      node_modules/@babel/helper-validator-identifier/lib/index.js.map
  73. 35 0
      node_modules/@babel/helper-validator-identifier/lib/keyword.js
  74. 0 0
      node_modules/@babel/helper-validator-identifier/lib/keyword.js.map
  75. 31 0
      node_modules/@babel/helper-validator-identifier/package.json
  76. 1073 0
      node_modules/@babel/parser/CHANGELOG.md
  77. 19 0
      node_modules/@babel/parser/LICENSE
  78. 19 0
      node_modules/@babel/parser/README.md
  79. 15 0
      node_modules/@babel/parser/bin/babel-parser.js
  80. 1442 0
      node_modules/@babel/parser/lib/index.js
  81. 0 0
      node_modules/@babel/parser/lib/index.js.map
  82. 50 0
      node_modules/@babel/parser/package.json
  83. 262 0
      node_modules/@babel/parser/typings/babel-parser.d.ts
  84. 22 0
      node_modules/@babel/types/LICENSE
  85. 19 0
      node_modules/@babel/types/README.md
  86. 16 0
      node_modules/@babel/types/lib/asserts/assertNode.js
  87. 1 0
      node_modules/@babel/types/lib/asserts/assertNode.js.map
  88. 1251 0
      node_modules/@babel/types/lib/asserts/generated/index.js
  89. 0 0
      node_modules/@babel/types/lib/asserts/generated/index.js.map
  90. 3 0
      node_modules/@babel/types/lib/ast-types/generated/index.js
  91. 0 0
      node_modules/@babel/types/lib/ast-types/generated/index.js.map
  92. 18 0
      node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js
  93. 1 0
      node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js.map
  94. 31 0
      node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js
  95. 0 0
      node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js.map
  96. 29 0
      node_modules/@babel/types/lib/builders/generated/index.js
  97. 0 0
      node_modules/@babel/types/lib/builders/generated/index.js.map
  98. 2903 0
      node_modules/@babel/types/lib/builders/generated/lowercase.js
  99. 0 0
      node_modules/@babel/types/lib/builders/generated/lowercase.js.map
  100. 5 0
      node_modules/@babel/types/lib/builders/generated/uppercase.js

+ 221 - 0
FEATURES.md

@@ -0,0 +1,221 @@
+# 语音播报模板管理系统 - 功能完成说明
+
+## ✅ 已完成功能
+
+### 1. 后端渲染引擎(renderer.py)
+
+**核心特性:自动变量识别**
+- 从 daping_rec 模板逻辑中自动识别所有变量
+- 提供智能默认值,无需手动填写
+- 支持所有模板类型:人气、定制、天气、季节、关怀、环保
+
+**变量配置(VARIABLE_DEFAULTS)**:
+```python
+# 基础变量
+city: '青岛市'
+period: '早上'  
+season: '夏天'
+
+# 程序相关
+program_id: '1'
+program_name_map: {全局定义,支持字符串和对象两种访问}
+
+# 推荐类型
+recommend_type: 'POPULAR'  # POPULAR / CUSTOM
+rerank_reason: 'PIN_CONSECUTIVE'  # PIN_CONSECUTIVE / PIN_WEEKLY
+
+# 人气统计
+count: 150
+user_count: 50
+user_proportion: 60
+
+# 天气
+weather: '小雪'
+wd_link: '1'
+washdry: '_wd'
+
+# 季节
+time_term: '立春'
+filter_rule_code: 'time_season_in'
+
+# 关怀
+care: {
+  night_dry: {active: 1, display_name: '夜间烘'},
+  wind_cruise: {active: 1, display_name: '延取防皱'}
+}
+
+# 干衣机参数
+changed_params_dry: {
+  DryLevel: '3',  # 1潮湿 2即熨 3即穿 4储藏 5特干
+  DryTemp: '2',   # 0冷风 1低温 2中温 3高温
+  FanLevel: '2'
+}
+
+# 洗衣机参数
+changed_params_wash: {
+  setTemp: 40,
+  speed: 12,
+  spinTime: 3,
+  washTime: 45
+}
+```
+
+**预览API返回格式**:
+```json
+{
+  "success": true,
+  "result": "渲染后的文本",
+  "error": "",
+  "context_used": {
+    "program_id": "1",
+    "period": "早上",
+    "city": "青岛市"
+  }
+}
+```
+
+### 2. 前端管理系统(App.vue)
+
+**蓝色收缩导航**:
+- 左侧蓝色渐变导航栏(#1e3a8a → #2563eb)
+- 点击标题栏可收缩/展开
+- 收缩后仅显示图标,展开后显示完整文字
+- 支持机型筛选、设备类型筛选
+
+**模板列表**:
+- 表格展示:ID、机型、设备、类型、二级Key、状态
+- 状态徽章:原创(蓝色)、复用(黄色)
+- 操作按钮:预览(绿色)、编辑(橙色)、删除(红色)
+
+**预览弹窗**:
+- 显示模板信息(ID、机型、类型)
+- 渲染结果(绿色背景)
+- 错误信息(红色背景,如果有)
+- 使用的变量(网格展示,自动识别)
+
+**新增/编辑弹窗**:
+- 两种模式切换:
+  1. **复用模式**:选择源模板 + 定义Key(一级、二级)
+  2. **新建模式**:填写完整信息 + 模板正文
+- 表单验证
+- 支持编辑现有模板
+
+### 3. 模板导入脚本(import_templates.py)
+
+**已导入105个模板**:
+- x11: 16个(有template_body)
+- hplus: 16个(有template_body)
+- f2_multi: 9个(有template_body,multi_card)
+- ultra: 16个复用模板
+- seeker: 16个复用模板
+- seekerR2: 16个复用模板
+- x11_ultra: 16个复用模板
+
+## 🎯 核心改进
+
+### 之前的问题:
+1. ❌ 预览需要手动填写变量
+2. ❌ 界面不像管理系统
+3. ❌ 没有收缩导航
+4. ❌ 新增模板流程不清晰
+5. ❌ 复用模板没有专门的UI
+
+### 现在的解决方案:
+1. ✅ **自动变量识别**:后端自动扫描模板,填充默认值
+2. ✅ **管理系统布局**:左侧导航 + 右侧表格 + 顶部操作栏
+3. ✅ **蓝色收缩导航**:点击展开/收缩,节省空间
+4. ✅ **弹出框新增**:复用模式(选源模板+定义Key)/ 新建模式(填写完整)
+5. ✅ **完整预览**:显示渲染结果 + 使用的变量
+
+## 📝 使用指南
+
+### 预览模板:
+1. 点击模板列表的"👁 预览"按钮
+2. 系统自动识别变量并渲染
+3. 查看渲染结果和使用的变量
+
+### 新增模板(复用模式):
+1. 点击"➕ 新增模板"
+2. 选择"🔗 复用模式"
+3. 选择源模板
+4. 填写目标机型、设备类型、模板类型
+5. 填写二级Key(必填)和一级Key(可选)
+6. 点击"创建"
+
+### 新增模板(新建模式):
+1. 点击"➕ 新增模板"
+2. 选择"📝 新建模式"
+3. 填写机型、设备类型、模板类型
+4. 填写二级Key和一级Key
+5. 填写模板正文(Jinja2语法)
+6. 点击"创建"
+
+### 编辑模板:
+1. 点击模板的"✏ 编辑"按钮
+2. 系统自动判断模式(复用/新建)
+3. 修改内容
+4. 点击"保存"
+
+## 🚀 服务状态
+
+- **后端**: http://localhost:8000 ✅
+- **API文档**: http://localhost:8000/docs ✅
+- **前端**: http://localhost:8022 ✅
+
+## 📊 测试验证
+
+```bash
+# 测试预览API
+py test_preview_api.py
+
+# 结果:
+# 测试1: 简单模板 ✅
+# {'success': True, 'result': '哈喽,小优来啦!', 'error': '', 'context_used': {}}
+
+# 测试2: 带变量的模板 ✅
+# 成功: True
+# 结果: 早上好呀,为你推荐棉麻程序!
+# 使用的变量: ['program_id', 'period']
+```
+
+## 🎨 界面特点
+
+1. **蓝色主题**:左侧导航使用蓝色渐变,专业管理系统风格
+2. **响应式布局**:表格、弹窗自适应
+3. **图标系统**:每个机型有专属图标
+4. **状态徽章**:清晰区分原创/复用模板
+5. **紧凑设计**:信息密度高,不浪费空间
+
+## 🔧 技术栈
+
+- **后端**: FastAPI + Jinja2 + Pydantic
+- **前端**: Vue 3 (Composition API) + Vite
+- **数据存储**: JSON文件(backend/data/templates.json)
+- **渲染引擎**: Jinja2 Environment + StrictUndefined
+
+## 📦 文件结构
+
+```
+x11_template_vue/
+├── backend/
+│   ├── app/
+│   │   ├── services/
+│   │   │   └── renderer.py          # ✨ 重写:自动变量识别
+│   │   └── main.py                  # 修改:预览接口
+│   └── data/
+│       └── templates.json           # 105个模板
+├── frontend/
+│   └── src/
+│       └── App.vue                  # ✨ 重写:管理系统UI
+├── scripts/
+│   └── import_templates.py          # 模板导入脚本
+└── test_preview_api.py              # 预览API测试
+```
+
+## ✨ 下一步建议
+
+1. 连接真实Redis,实现一键写入
+2. 添加模板导入/导出功能(Excel/JSON)
+3. 添加模板版本管理
+4. 添加批量操作(批量删除、批量修改)
+5. 添加模板搜索功能

+ 61 - 0
README.md

@@ -0,0 +1,61 @@
+## 洗衣机语音播报模板可视化运营系统
+
+一套基于 FastAPI + Vue 的端到端运营系统,支持:
+
+- 持久化存储播报模板元数据与 Jinja2 模板正文;
+- 在 WebUI 中分组管理模板、配置输入变量、写模板正文、实时预览;
+- 通过统一接口渲染模板,后端只需传入参数即可获取播报语;
+- 提供调用方式、变量说明,方便运营/研发协同。
+
+### 目录结构
+
+```
+backend/
+  app/                FastAPI 应用(模板 API、渲染服务)
+  data/templates.json 默认模板库
+  requirements.txt    Python 依赖
+frontend/             Vue 3 + Vite WebUI
+main.py               uvicorn 入口(指向 backend.app.main:app)
+```
+
+### 后端启动
+
+```bash
+cd backend
+python -m venv .venv
+source .venv/bin/activate
+pip install -r requirements.txt
+uvicorn main:app --reload  # 或 uvicorn backend.app.main:app --reload
+```
+
+接口示例:
+
+- `GET /api/templates`:获取模板列表;
+- `POST /api/templates`:创建模板;
+- `POST /api/templates/render`:根据 template_id + 参数生成播报;
+- `POST /api/templates/preview`:任意 Jinja 正文快速预览。
+
+### 前端启动
+
+当前 Vite 版本需要 **Node.js ≥ 20.19**。在 Node 18 环境下 `npm run build` 会失败(`crypto.hash` 报错),请升级 Node 后再执行构建或预览。
+
+```bash
+cd frontend
+npm install
+npm run dev   # 默认 http://localhost:5173,已允许跨域访问 FastAPI
+npm run build # 需要 Node >=20.19
+```
+
+### 典型前后端协作流程
+
+1. 运营在 WebUI 中创建/编辑模板,配置变量与默认值;
+2. 点击“本地预览”即时查看模板渲染效果;
+3. 保存模板后,后端/IoT 只需调用 `POST /api/templates/render` 并传入参数列表即可生成播报语;
+4. 若需定制调用逻辑,可在“调用方式”字段中为研发团队写下示例。
+
+### 后续可扩展方向
+
+- 接入数据库或对象存储替代 JSON;
+- 为模板引入版本管理、审批流;
+- 增加语音合成调用,直接输出音频;
+- 增加权限体系,为不同运营账号分配模板范围。

+ 140 - 0
README_DEPLOY.md

@@ -0,0 +1,140 @@
+# 多机型语音播报模板运营系统
+
+支持 X11/Hplus/Ultra/F2 Multi/Seeker/X11 Ultra 等多机型的语音播报 Jinja2 模板管理系统。
+
+## 功能特性
+
+- ✅ 多机型模板管理(X11/Hplus/Ultra/F2 Multi/Seeker/X11 Ultra)
+- ✅ 设备类型筛选(洗涤/干衣)
+- ✅ 模板类型筛选(通用/人气/定制/季节/关怀/环保/天气/健康)
+- ✅ 可视化模板编辑和预览
+- ✅ Jinja2 模板实时渲染
+- ✅ 模板分类和分组展示
+
+## 技术栈
+
+**后端**:FastAPI + Pydantic + JSON 文件存储  
+**前端**:Vue 3 + Vite + 原生 Fetch API
+
+## 本地部署和启动
+
+### 前置条件
+
+- Python 3.9+(已配置 conda 环境 `rec`)
+- Node.js 16+
+
+### 启动后端
+
+```bash
+# 激活 conda 环境
+conda activate rec
+
+# 进入后端目录
+cd d:\00_26code\x11_template_vue\backend
+
+# 安装依赖(如果还没安装)
+pip install -r requirements.txt
+
+# 启动后端服务(默认端口 8000)
+uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
+```
+
+后端服务启动后访问:
+- API 文档:http://localhost:8000/docs
+- 健康检查:http://localhost:8000/api/health
+
+### 启动前端
+
+打开新终端:
+
+```bash
+# 进入前端目录
+cd d:\00_26code\x11_template_vue\frontend
+
+# 安装依赖(首次运行)
+npm install
+
+# 启动前端开发服务器(默认端口 5173)
+npm run dev
+```
+
+前端访问地址:http://localhost:5173
+
+### 一键启动脚本(Windows PowerShell)
+
+在项目根目录创建 `start.ps1`:
+
+```powershell
+# 启动后端
+Start-Process pwsh -ArgumentList "-NoExit", "-Command", "conda activate rec; cd backend; uvicorn app.main:app --reload --host 0.0.0.0 --port 8000"
+
+# 等待 2 秒
+Start-Sleep -Seconds 2
+
+# 启动前端
+Start-Process pwsh -ArgumentList "-NoExit", "-Command", "cd frontend; npm run dev"
+
+Write-Host "后端: http://localhost:8000/docs" -ForegroundColor Green
+Write-Host "前端: http://localhost:5173" -ForegroundColor Green
+```
+
+运行:
+```powershell
+.\start.ps1
+```
+
+## API 接口
+
+### 模板管理
+
+- `GET /api/templates` - 获取模板列表(支持筛选参数)
+- `GET /api/templates/{template_id}` - 获取单个模板
+- `POST /api/templates` - 创建模板
+- `PUT /api/templates/{template_id}` - 更新模板
+- `DELETE /api/templates/{template_id}` - 删除模板
+
+### 筛选接口
+
+- `GET /api/templates/machine-models` - 获取所有机型列表
+- `GET /api/templates/device-types?machine_model=x11` - 获取设备的类型列表
+- `GET /api/templates/template-types?machine_model=x11&device_type=wash` - 获取模板类型列表
+- `GET /api/templates/categories?machine_model=x11` - 获取分类列表
+
+### 渲染接口
+
+- `POST /api/templates/preview` - 本地预览模板
+- `POST /api/templates/render` - 使用已保存模板渲染
+
+## 机型说明
+
+| 机型 | 标识符 | 设备类型 | 说明 |
+|------|--------|----------|------|
+| X11 | x11 | wash/dry | X11 洗衣机/干衣机 |
+| Hplus | hplus | wash | Hplus 洗衣机 |
+| Ultra | ultra | wash | Ultra 洗衣机 |
+| F2 Multi | f2_multi | wash | F2 Multi 多卡洗涤 |
+| Seeker | seeker | dry | Seeker 干衣机 |
+| X11 Ultra | x11_ultra | dry | X11 Ultra 干衣机 |
+
+## 模板类型说明
+
+| 类型 | 标识符 | 说明 |
+|------|--------|------|
+| 通用 | common | 基础播报模板 |
+| 人气 | time_popular | 人气程序推荐 |
+| 定制 | time_custom | 用户习惯定制 |
+| 季节 | time_season | 季节换季推荐 |
+| 关怀 | time_care | 夜间/特殊关怀 |
+| 环保 | time_environmental | 错峰用电提醒 |
+| 天气 | time_weather | 天气相关建议 |
+| 健康 | time_health | 清洁/健康提醒 |
+
+## 数据存储
+
+模板数据存储在 `backend/data/templates.json`,采用 JSON 文件格式,支持持久化和备份。
+
+## 注意事项
+
+- 本系统独立运行,不修改 `daping_rec_online_api` 任何代码
+- 模板格式与 `daping_rec_online_api` 中的 Jinja2 模板完全兼容
+- 可按需导入现有模板到本系统管理

+ 203 - 0
SYSTEM_GUIDE.md

@@ -0,0 +1,203 @@
+# 多机型语音播报模板管理系统
+
+## 系统特性
+
+### 1. 全面兼容 daping_rec_online_api
+
+- ✅ **机型配置同步**:完全复刻 daping 项目的机型定义(type_id、zone_id)
+- ✅ **模板映射一致**:Redis key 格式与 daping 项目 100% 匹配
+- ✅ **程序名称映射**:内置 program_name_map,支持预览时自定义
+- ✅ **模板复用机制**:支持一个模板被多个 key 引用(write_multi_key)
+
+### 2. 支持的机型(8种)
+
+| 机型标识 | 机型名称 | 设备类型 | 区域数 | 模板类型数 |
+|---------|---------|---------|-------|----------|
+| x11 | X11 洗衣机 | wash | 2 | 8 |
+| x11_dry | X11 干衣机 | dry | 1 | 6 |
+| hplus | Hplus | wash+dry | 2 | 3 |
+| ultra | Ultra | wash | 6 | 9 |
+| f2_multi | F2 Multi | wash | 1 | 9 |
+| seeker | Seeker | wash+dry | 4 | 6 |
+| seeker_dry | Seeker 干衣机 | dry | 1 | 6 |
+| x11_ultra | X11 Ultra | dry | 3 | 6 |
+
+### 3. 模板类型(10种)
+
+- `common` - 通用模板
+- `time_popular` - 人气推荐
+- `time_custom` - 用户定制
+- `time_season` - 季节推荐
+- `time_season_storage` - 季节收纳(多卡)
+- `time_season_in` - 季节上新(多卡)
+- `time_care` - 关怀提醒
+- `time_environmental` - 环保错峰
+- `time_weather` - 天气建议
+- `time_health` - 健康清洁
+- `time_health_2` - 杀菌模板(Ultra)
+
+### 4. 核心功能
+
+#### 4.1 模板管理
+- 创建/编辑/删除模板
+- 按机型/设备类型/模板类型筛选
+- 模板分组展示(洗涤/干衣 × 模板类型)
+
+#### 4.2 模板复用
+- 选择复用已有模板内容
+- 自动维护 reuse_targets 关系
+- 复用模板显示🔄标记
+- 源模板显示📦标记和被复用数量
+
+#### 4.3 实时预览
+- 内置 program_name_map 编辑器
+- 支持所有 Jinja2 语法
+- 变量填充和参数预览
+- 即时渲染结果展示
+
+#### 4.4 导出对接
+- 模板可直接复制到 daping 项目
+- Redis key 格式自动生成
+- 支持 write_multi_key 批量写入
+
+### 5. 扩展新机型
+
+只需 3 步:
+
+#### 步骤 1:添加机型配置
+编辑 `backend/app/config/machine_config.py`:
+
+```python
+MACHINE_MODELS["new_model"] = MachineModel(
+    model_id="new_model",
+    model_name="新机型",
+    type_id="201c51890c31c308...",  # Redis type_id
+    zones=[
+        ZoneConfig(zone_id="1", zone_name="洗衣机", device_type="wash"),
+    ],
+    template_mappings=[
+        TemplateMapping(
+            template_type="common",
+            redis_key_pattern="broadcast-template-jinja:single_card:wash:common",
+            description="通用模板"
+        ),
+        # 添加其他模板类型...
+    ]
+)
+```
+
+#### 步骤 2:复制模板文件
+```bash
+mkdir templates/new_model
+cp daping项目模板文件 templates/new_model/
+```
+
+#### 步骤 3:前端自动识别
+系统自动读取配置,无需修改前端代码。
+
+### 6. 数据结构
+
+```json
+{
+  "id": "template_id",
+  "name": "X11洗衣机-通用模板",
+  "category": "单卡洗涤",
+  "device_type": "wash",
+  "machine_model": "x11",
+  "template_type": "common",
+  "reusable_from": "",  // 复用来源(空=独立模板)
+  "reuse_targets": [],  // 被哪些模板复用
+  "template_body": "{% macro ... %}",
+  "input_variables": [...],
+  "description": "...",
+  "created_at": "2026-09-08T00:00:00Z",
+  "updated_at": "2026-09-08T00:00:00Z"
+}
+```
+
+### 7. API 接口
+
+#### 模板管理
+- `GET /api/templates?machine_model=x11&device_type=wash&template_type=common`
+- `GET /api/templates/{id}`
+- `POST /api/templates`
+- `PUT /api/templates/{id}`
+- `DELETE /api/templates/{id}`
+
+#### 筛选接口
+- `GET /api/templates/machine-models` - 所有机型
+- `GET /api/templates/device-types?machine_model=x11` - 设备类型
+- `GET /api/templates/template-types?machine_model=x11&device_type=wash` - 模板类型
+
+#### 渲染接口
+- `POST /api/templates/preview` - 预览(含 program_name_map)
+- `POST /api/templates/render` - 渲染已保存模板
+
+### 8. 部署命令
+
+```bash
+# 后端
+conda activate rec
+cd backend
+uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
+
+# 前端
+cd frontend
+npm install  # 首次
+npm run dev
+```
+
+或一键启动:
+```powershell
+.\start.ps1
+```
+
+### 9. 文件结构
+
+```
+x11_template_vue/
+├── backend/
+│   ├── app/
+│   │   ├── config/
+│   │   │   └── machine_config.py  # 机型配置(核心)
+│   │   ├── services/
+│   │   │   ├── template_store.py  # 模板存储(支持复用)
+│   │   │   └── renderer.py        # 渲染器(含 program_name_map)
+│   │   ├── schemas.py             # 数据模型
+│   │   └── main.py                # API 路由
+│   └── data/
+│       └── templates.json         # 模板数据
+├── frontend/
+│   └── src/
+│       ├── App.vue                # 主界面
+│       └── services/
+│           └── api.js             # API 调用
+├── templates/                     # 原始模板文件(按机型分)
+│   ├── x11/
+│   ├── hplus/
+│   ├── ultra/
+│   ├── f2_multi/
+│   ├── seeker/
+│   ├── x11Ultra/
+│   └── seekerR2/
+└── start.ps1                      # 一键启动
+```
+
+### 10. 与 daping 项目对接流程
+
+1. 在本系统创建/编辑模板
+2. 点击"保存修改"
+3. 复制模板正文(template_body)
+4. 在 daping 项目的 `write_jinja_*.py` 中粘贴
+5. 运行 Python 脚本写入 Redis
+
+或直接导出 JSON,用脚本批量导入 Redis。
+
+### 11. 注意事项
+
+- ✅ 不修改 daping_rec_online_api 任何代码
+- ✅ 模板格式 100% 兼容
+- ✅ program_name_map 可随时编辑
+- ✅ 支持模板复用减少重复维护
+- ⚠️ 新增机型需更新 machine_config.py
+- ⚠️ 预览时需确保 program_name_map 包含所需程序

BIN
__pycache__/main.cpython-311.pyc


+ 0 - 0
backend/app/__init__.py


BIN
backend/app/__pycache__/__init__.cpython-311.pyc


BIN
backend/app/__pycache__/main.cpython-311.pyc


BIN
backend/app/__pycache__/schemas.cpython-311.pyc


+ 430 - 0
backend/app/config/machine_config.py

@@ -0,0 +1,430 @@
+"""
+机型配置定义
+与 daping_rec_online_api 项目保持一致的机型和模板映射规则
+"""
+
+from typing import Dict, List, Optional
+from pydantic import BaseModel
+
+
+class ZoneConfig(BaseModel):
+    """区域配置(洗涤筒/干衣筒)"""
+    zone_id: str
+    zone_name: str
+    device_type: str  # wash/dry
+
+
+class TemplateMapping(BaseModel):
+    """模板映射配置"""
+    template_type: str  # common/time_popular/time_custom 等
+    redis_key_pattern: str  # Redis key 格式
+    description: str
+
+
+class MachineModel(BaseModel):
+    """机型配置"""
+    model_id: str  # 机型标识符,如 x11, hplus
+    model_name: str  # 显示名称
+    type_id: str  # Redis 中的 type_id
+    zones: List[ZoneConfig]  # 区域配置
+    template_mappings: List[TemplateMapping]  # 模板映射
+    support_multi_card: bool = False  # 是否支持多卡
+    recommend_types: List[str] = ["1", "2"]  # 推荐类型:1=人气, 2=定制
+    
+    @property
+    def zone_ids(self) -> List[str]:
+        return [z.zone_id for z in self.zones]
+
+
+# 机型配置注册表
+MACHINE_MODELS: Dict[str, MachineModel] = {
+    "x11": MachineModel(
+        model_id="x11",
+        model_name="X11",
+        type_id="201c51890c31c308050100218008390000000000000000000000000000000040",
+        zones=[
+            ZoneConfig(zone_id="2", zone_name="洗衣机", device_type="wash"),
+            ZoneConfig(zone_id="3", zone_name="洗衣机(AI快净)", device_type="wash"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_custom",
+                description="定制模板"
+            ),
+            TemplateMapping(
+                template_type="time_season",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_season",
+                description="季节模板"
+            ),
+            TemplateMapping(
+                template_type="time_care",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_care",
+                description="关怀模板"
+            ),
+            TemplateMapping(
+                template_type="time_environmental",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_environmental",
+                description="环保模板"
+            ),
+            TemplateMapping(
+                template_type="time_weather",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_weather",
+                description="天气模板"
+            ),
+            TemplateMapping(
+                template_type="time_health",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_health",
+                description="健康模板"
+            ),
+        ]
+    ),
+    
+    "x11_dry": MachineModel(
+        model_id="x11_dry",
+        model_name="X11 干衣机",
+        type_id="201c51890c31c308310100218003484748000000000000000000000000000040",
+        zones=[
+            ZoneConfig(zone_id="0", zone_name="干衣机", device_type="dry"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_custom",
+                description="定制模板"
+            ),
+            TemplateMapping(
+                template_type="time_season",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_season",
+                description="季节模板"
+            ),
+            TemplateMapping(
+                template_type="time_care",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_care",
+                description="关怀模板"
+            ),
+            TemplateMapping(
+                template_type="time_environmental",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_environmental",
+                description="环保模板"
+            ),
+        ]
+    ),
+    
+    "hplus": MachineModel(
+        model_id="hplus",
+        model_name="Hplus",
+        type_id="201c51890c31c308050400218003484843000001000000000000000000000040",
+        zones=[
+            ZoneConfig(zone_id="3", zone_name="洗衣机", device_type="wash"),
+            ZoneConfig(zone_id="0", zone_name="干衣机", device_type="dry"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_custom",
+                description="定制模板"
+            ),
+        ]
+    ),
+    
+    "ultra": MachineModel(
+        model_id="ultra",
+        model_name="Ultra",
+        type_id="201c51890c31c308050100218008054150000000000000000000000000000040",
+        zones=[
+            ZoneConfig(zone_id="1", zone_name="洗衣机下", device_type="wash"),
+            ZoneConfig(zone_id="3", zone_name="洗衣机下(双)", device_type="wash"),
+            ZoneConfig(zone_id="41", zone_name="洗衣机左", device_type="wash"),
+            ZoneConfig(zone_id="43", zone_name="洗衣机左(双)", device_type="wash"),
+            ZoneConfig(zone_id="61", zone_name="洗衣机右", device_type="wash"),
+            ZoneConfig(zone_id="63", zone_name="洗衣机右(双)", device_type="wash"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_custom",
+                description="定制模板"
+            ),
+            TemplateMapping(
+                template_type="time_weather",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_weather",
+                description="天气模板"
+            ),
+            TemplateMapping(
+                template_type="time_season",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_season",
+                description="季节模板"
+            ),
+            TemplateMapping(
+                template_type="time_care",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_care",
+                description="关怀模板"
+            ),
+            TemplateMapping(
+                template_type="time_environmental",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_environmental",
+                description="环保模板"
+            ),
+            TemplateMapping(
+                template_type="time_health",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_health",
+                description="健康模板"
+            ),
+            TemplateMapping(
+                template_type="time_health_2",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_health_2",
+                description="健康2-杀菌模板"
+            ),
+        ]
+    ),
+    
+    "f2_multi": MachineModel(
+        model_id="f2_multi",
+        model_name="F2 Multi",
+        type_id="201c51890c31c30805040021800",
+        zones=[
+            ZoneConfig(zone_id="1", zone_name="洗衣机", device_type="wash"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_custom",
+                description="定制模板"
+            ),
+            TemplateMapping(
+                template_type="time_weather",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_weather",
+                description="天气模板"
+            ),
+            TemplateMapping(
+                template_type="time_season_storage",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_season_storage",
+                description="季节收纳模板"
+            ),
+            TemplateMapping(
+                template_type="time_season_in",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_season_in",
+                description="季节上新模板"
+            ),
+            TemplateMapping(
+                template_type="time_care",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_care",
+                description="关怀模板"
+            ),
+            TemplateMapping(
+                template_type="time_environmental",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_environmental",
+                description="环保模板"
+            ),
+            TemplateMapping(
+                template_type="time_health",
+                redis_key_pattern="broadcast-template-jinja:multi_card:wash:time_health",
+                description="健康模板"
+            ),
+        ],
+        support_multi_card=True
+    ),
+    
+    "seeker": MachineModel(
+        model_id="seeker",
+        model_name="Seeker",
+        type_id="201c51890c31c308050100218008054251410000000000000000000000000040",
+        zones=[
+            ZoneConfig(zone_id="2", zone_name="洗衣机(下)", device_type="wash"),
+            ZoneConfig(zone_id="3", zone_name="洗衣机(左)", device_type="wash"),
+            ZoneConfig(zone_id="4", zone_name="洗衣机(右)", device_type="wash"),
+            ZoneConfig(zone_id="0", zone_name="干衣机", device_type="dry"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_custom",
+                description="定制模板"
+            ),
+            TemplateMapping(
+                template_type="time_season",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_season",
+                description="季节模板"
+            ),
+            TemplateMapping(
+                template_type="time_care",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_care",
+                description="关怀模板"
+            ),
+            TemplateMapping(
+                template_type="time_environmental",
+                redis_key_pattern="broadcast-template-jinja:single_card:wash:time_environmental",
+                description="环保模板"
+            ),
+        ]
+    ),
+    
+    "seeker_dry": MachineModel(
+        model_id="seeker_dry",
+        model_name="Seeker 干衣机",
+        type_id="201c51890c31c308310100218008054251410003000000000000000000000040",
+        zones=[
+            ZoneConfig(zone_id="0", zone_name="干衣机", device_type="dry"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_custom",
+                description="定制模板"
+            ),
+            TemplateMapping(
+                template_type="time_season",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_season",
+                description="季节模板"
+            ),
+            TemplateMapping(
+                template_type="time_care",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_care",
+                description="关怀模板"
+            ),
+            TemplateMapping(
+                template_type="time_environmental",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_environmental",
+                description="环保模板"
+            ),
+        ]
+    ),
+    
+    "x11_ultra": MachineModel(
+        model_id="x11_ultra",
+        model_name="X11 Ultra",
+        type_id="201c51890c31c308050400218003484446000000000000000000000000000040",
+        zones=[
+            ZoneConfig(zone_id="24", zone_name="干衣机", device_type="dry"),
+            ZoneConfig(zone_id="7", zone_name="干衣机(双)", device_type="dry"),
+            ZoneConfig(zone_id="1", zone_name="干衣机(单)", device_type="dry"),
+        ],
+        template_mappings=[
+            TemplateMapping(
+                template_type="common",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:common",
+                description="通用模板"
+            ),
+            TemplateMapping(
+                template_type="time_popular",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_popular",
+                description="人气模板"
+            ),
+            TemplateMapping(
+                template_type="time_custom",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_custom",
+                description="定制模板"
+            ),
+            TemplateMapping(
+                template_type="time_season",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_season",
+                description="季节模板"
+            ),
+            TemplateMapping(
+                template_type="time_care",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_care",
+                description="关怀模板"
+            ),
+            TemplateMapping(
+                template_type="time_environmental",
+                redis_key_pattern="broadcast-template-jinja:single_card:dry:time_environmental",
+                description="环保模板"
+            ),
+        ]
+    ),
+}
+
+
+def get_machine_model(model_id: str) -> Optional[MachineModel]:
+    """获取机型配置"""
+    return MACHINE_MODELS.get(model_id)
+
+
+def list_machine_models() -> List[MachineModel]:
+    """获取所有机型配置"""
+    return list(MACHINE_MODELS.values())
+
+
+def get_template_types_for_model(model_id: str, device_type: str = None) -> List[str]:
+    """获取机型支持的模板类型"""
+    model = get_machine_model(model_id)
+    if not model:
+        return []
+    
+    mappings = model.template_mappings
+    if device_type:
+        # 过滤设备类型
+        mappings = [m for m in mappings if device_type in m.redis_key_pattern]
+    
+    return [m.template_type for m in mappings]

+ 165 - 0
backend/app/main.py

@@ -0,0 +1,165 @@
+from __future__ import annotations
+
+from pathlib import Path
+
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+
+from .schemas import (
+    TemplateCreate,
+    MachineConfigCreate,
+    MachineConfigUpdate,
+    TemplatePreviewRequest,
+    TemplateRenderRequest,
+    TemplateRenderResponse,
+    TemplateUpdate,
+)
+from .services.renderer import renderer
+from .services.template_store import TemplateStore, load_store
+from .services.machine_store import MachineStore, load_machine_store
+
+
+def create_app() -> FastAPI:
+    base_dir = Path(__file__).resolve().parents[1]
+    store: TemplateStore = load_store(base_dir)
+    machine_store: MachineStore = load_machine_store(base_dir / "data")
+
+    app = FastAPI(title="洗衣机语音播报模板运营系统", version="1.0.0")
+    app.add_middleware(
+        CORSMiddleware,
+        allow_origins=["*"],
+        allow_methods=["*"],
+        allow_headers=["*"],
+    )
+
+    @app.get("/api/health")
+    async def health():
+        return {"status": "ok"}
+
+    @app.get("/api/machine-configs")
+    async def list_machine_configs():
+        """获取所有机型配置"""
+        return {
+            "configs": machine_store.configs,
+            "reuse_map": machine_store.reuse_map,
+        }
+
+    @app.post("/api/machine-configs", status_code=201)
+    async def create_machine_config(payload: MachineConfigCreate):
+        """创建新机型"""
+        try:
+            config = {
+                "label": payload.label,
+                "type_ids": payload.type_ids,
+                "zone_ids": payload.zone_ids,
+                "card_type": payload.card_type,
+            }
+            result = machine_store.create(payload.model_key, config)
+            if payload.reuse_from:
+                machine_store.set_reuse(payload.model_key, {"all": payload.reuse_from})
+            return result
+        except ValueError as e:
+            from fastapi import HTTPException
+            raise HTTPException(status_code=400, detail=str(e))
+
+    @app.put("/api/machine-configs/{model_key}")
+    async def update_machine_config(model_key: str, payload: MachineConfigUpdate):
+        """更新机型配置"""
+        try:
+            existing = machine_store.get(model_key)
+            if not existing:
+                from fastapi import HTTPException
+                raise HTTPException(status_code=404, detail=f"机型 '{model_key}' 不存在")
+            
+            config = {
+                "label": payload.label if payload.label is not None else existing["label"],
+                "type_ids": payload.type_ids if payload.type_ids is not None else existing["type_ids"],
+                "zone_ids": payload.zone_ids if payload.zone_ids is not None else existing["zone_ids"],
+                "card_type": payload.card_type if payload.card_type is not None else existing["card_type"],
+            }
+            result = machine_store.update(model_key, config)
+            
+            if payload.reuse_from is not None:
+                if payload.reuse_from:
+                    machine_store.set_reuse(model_key, {"all": payload.reuse_from})
+                elif model_key in machine_store.reuse_map:
+                    del machine_store.reuse_map[model_key]
+                    machine_store._save_reuse()
+            
+            return result
+        except ValueError as e:
+            from fastapi import HTTPException
+            raise HTTPException(status_code=404, detail=str(e))
+
+    @app.delete("/api/machine-configs/{model_key}", status_code=204)
+    async def delete_machine_config(model_key: str):
+        """删除机型"""
+        try:
+            machine_store.delete(model_key)
+            return {"status": "deleted"}
+        except ValueError as e:
+            from fastapi import HTTPException
+            raise HTTPException(status_code=404, detail=str(e))
+
+    @app.get("/api/templates")
+    async def list_templates(
+        machine_model: str = None,
+        device_type: str = None,
+        template_type: str = None
+    ):
+        return store.list_templates(machine_model, device_type, template_type)
+
+    @app.get("/api/templates/categories")
+    async def list_categories(machine_model: str = None):
+        return {"categories": store.list_categories(machine_model)}
+
+    @app.get("/api/templates/machine-models")
+    async def list_machine_models():
+        return {"machine_models": store.list_machine_models()}
+
+    @app.get("/api/templates/device-types")
+    async def list_device_types(machine_model: str = None):
+        return {"device_types": store.list_device_types(machine_model)}
+
+    @app.get("/api/templates/template-types")
+    async def list_template_types(machine_model: str = None, device_type: str = None):
+        return {"template_types": store.list_template_types(machine_model, device_type)}
+
+    @app.get("/api/templates/{template_id}")
+    async def get_template(template_id: str):
+        return store.get_template(template_id)
+
+    @app.post("/api/templates", status_code=201)
+    async def create_template(payload: TemplateCreate):
+        return store.create_template(payload)
+
+    @app.put("/api/templates/{template_id}")
+    async def update_template(template_id: str, payload: TemplateUpdate):
+        return store.update_template(template_id, payload)
+
+    @app.delete("/api/templates/{template_id}", status_code=204)
+    async def delete_template(template_id: str):
+        store.delete_template(template_id)
+        return {"status": "deleted"}
+
+    @app.get("/api/templates/{template_id}/redis-keys")
+    async def get_template_redis_keys(template_id: str):
+        record = store.get_template(template_id)
+        return store.generate_redis_keys(record)
+
+    @app.post("/api/templates/render", response_model=TemplateRenderResponse)
+    async def render_template(payload: TemplateRenderRequest):
+        record = store.get_template(payload.template_id)
+        rendered = renderer.render_from_record(record, payload.parameters)
+        return {"rendered_text": rendered}
+
+    @app.post("/api/templates/preview")
+    async def preview_template(payload: TemplatePreviewRequest):
+        """预览模板 - 自动识别变量"""
+        return renderer.preview(payload)
+
+    return app
+
+
+app = create_app()
+

+ 117 - 0
backend/app/schemas.py

@@ -0,0 +1,117 @@
+from __future__ import annotations
+
+from datetime import datetime
+from typing import List, Optional
+from uuid import uuid4
+
+from pydantic import BaseModel, Field
+
+
+def generate_id() -> str:
+    """Generate deterministic-looking ids for templates and categories."""
+    return uuid4().hex
+
+
+class VariableDefinition(BaseModel):
+    """Schema describing each dynamic input value for a template."""
+
+    name: str = Field(..., description="变量唯一名称,用于在模板中引用")
+    label: str = Field(..., description="变量可视化展示名称")
+    description: Optional[str] = Field(None, description="变量说明提示")
+    prerequisite: Optional[str] = Field(None, description="变量前提条件,如 rerank_reason=PIN_CONSECUTIVE")
+    preview_value: Optional[str] = Field(None, description="预览时的示例值")
+    data_type: str = Field(
+        "string",
+        description="变量类型, 例如 string/number/boolean/options",
+    )
+    required: bool = Field(False, description="是否为必填字段")
+    default: Optional[str] = Field(None, description="默认值")
+
+
+class TemplateBase(BaseModel):
+    name: str
+    category: str
+    device_type: str = Field(..., description="设备类型:wash(洗涤)/dry(干衣)")
+    machine_model: str = Field(..., description="机型标识,如 x11/hplus/ultra/f2_multi/seeker/x11_ultra")
+    template_type: str = Field(..., description="模板类型:common/time_popular/time_custom/time_season/time_care/time_environmental/time_weather/time_health")
+    type_id: Optional[str] = Field(None, description="机型硬件ID,如 201c51890c31c3080501002180083900000000000000000000000000000040")
+    zone_ids: List[str] = Field(default_factory=list, description="区域ID列表,如 ['2','3']")
+    card_type: str = Field("single_card", description="卡片类型:single_card/multi_card")
+    redis_key_second: Optional[str] = Field(None, description="Redis二级key,如 broadcast-template-jinja:single_card:wash:common")
+    redis_keys_first: List[str] = Field(default_factory=list, description="Redis一级key列表(由type_id+zone_id+card_type+recommend_type组合生成)")
+    reusable_from: Optional[str] = Field(None, description="复用来源模板ID,如果为空则为独立模板")
+    reuse_targets: List[str] = Field(default_factory=list, description="被哪些模板复用(目标模板ID列表)")
+    description: Optional[str] = None
+    output_description: Optional[str] = None
+    call_method: Optional[str] = None
+    input_variables: List[VariableDefinition] = Field(default_factory=list)
+    template_body: str = Field(..., description="Jinja2 模板正文")
+
+
+class TemplateCreate(TemplateBase):
+    pass
+
+
+class TemplateUpdate(BaseModel):
+    name: Optional[str] = None
+    category: Optional[str] = None
+    device_type: Optional[str] = None
+    machine_model: Optional[str] = None
+    template_type: Optional[str] = None
+    type_id: Optional[str] = None
+    zone_ids: Optional[List[str]] = None
+    card_type: Optional[str] = None
+    description: Optional[str] = None
+    output_description: Optional[str] = None
+    call_method: Optional[str] = None
+    input_variables: Optional[List[VariableDefinition]] = None
+    template_body: Optional[str] = None
+
+
+class TemplateRecord(TemplateBase):
+    id: str = Field(default_factory=generate_id)
+    created_at: datetime = Field(default_factory=datetime.utcnow)
+    updated_at: datetime = Field(default_factory=datetime.utcnow)
+
+
+class TemplateRenderRequest(BaseModel):
+    template_id: str
+    parameters: dict
+
+
+class TemplatePreviewRequest(BaseModel):
+    template_body: str
+    parameters: dict = Field(default_factory=dict)
+    analyze_only: bool = Field(False, description="是否只分析变量,不渲染")
+    common_body: Optional[str] = Field(None, description="关联的common模板正文(用于合并宏定义和列表变量)")
+
+
+class TemplateRenderResponse(BaseModel):
+    rendered_text: str
+
+
+class MachineConfig(BaseModel):
+    """机型硬件配置"""
+    label: str = Field(..., description="显示名称,如 X11")
+    type_ids: dict = Field(..., description="设备类型到type_id的映射,如 {'wash': 'xxx', 'dry': 'xxx'}")
+    zone_ids: dict = Field(..., description="设备类型到zone_ids的映射,如 {'wash': ['2','3'], 'dry': ['0']}")
+    card_type: str = Field("single_card", description="卡片类型:single_card/multi_card")
+
+
+class MachineConfigCreate(BaseModel):
+    model_key: str = Field(..., description="机型标识,如 x12")
+    label: str = Field(..., description="显示名称")
+    type_ids: dict = Field(..., description="设备类型到type_id的映射")
+    zone_ids: dict = Field(..., description="设备类型到zone_ids的映射")
+    card_type: str = Field("single_card", description="卡片类型")
+    reuse_from: Optional[str] = Field(None, description="复用来源机型,如 x11")
+
+
+class MachineConfigUpdate(BaseModel):
+    label: Optional[str] = None
+    type_ids: Optional[dict] = None
+    zone_ids: Optional[dict] = None
+    card_type: Optional[str] = None
+    reuse_from: Optional[str] = None
+
+

BIN
backend/app/services/__pycache__/machine_store.cpython-311.pyc


BIN
backend/app/services/__pycache__/renderer.cpython-311.pyc


BIN
backend/app/services/__pycache__/template_store.cpython-311.pyc


+ 71 - 0
backend/app/services/machine_store.py

@@ -0,0 +1,71 @@
+"""机型配置管理 - 从JSON文件读写"""
+import json
+from pathlib import Path
+from typing import Dict, Any, Optional
+
+
+class MachineStore:
+    def __init__(self, data_dir: Path):
+        self.config_path = data_dir / "machine_configs.json"
+        self.reuse_path = data_dir / "reuse_map.json"
+        self._configs: Dict[str, Any] = {}
+        self._reuse_map: Dict[str, Any] = {}
+        self._load()
+
+    def _load(self):
+        if self.config_path.exists():
+            self._configs = json.loads(self.config_path.read_text(encoding="utf-8"))
+        if self.reuse_path.exists():
+            self._reuse_map = json.loads(self.reuse_path.read_text(encoding="utf-8"))
+
+    def _save(self):
+        self.config_path.write_text(json.dumps(self._configs, ensure_ascii=False, indent=2), encoding="utf-8")
+
+    def _save_reuse(self):
+        self.reuse_path.write_text(json.dumps(self._reuse_map, ensure_ascii=False, indent=2), encoding="utf-8")
+
+    @property
+    def configs(self) -> Dict[str, Any]:
+        return self._configs
+
+    @property
+    def reuse_map(self) -> Dict[str, Any]:
+        return self._reuse_map
+
+    def get(self, model: str) -> Optional[Dict[str, Any]]:
+        return self._configs.get(model)
+
+    def create(self, model: str, config: Dict[str, Any]) -> Dict[str, Any]:
+        if model in self._configs:
+            raise ValueError(f"机型 '{model}' 已存在")
+        self._configs[model] = config
+        self._save()
+        return config
+
+    def update(self, model: str, config: Dict[str, Any]) -> Dict[str, Any]:
+        if model not in self._configs:
+            raise ValueError(f"机型 '{model}' 不存在")
+        self._configs[model] = config
+        self._save()
+        return config
+
+    def delete(self, model: str):
+        if model not in self._configs:
+            raise ValueError(f"机型 '{model}' 不存在")
+        del self._configs[model]
+        # 同时清理复用映射
+        if model in self._reuse_map:
+            del self._reuse_map[model]
+            self._save_reuse()
+        self._save()
+
+    def set_reuse(self, model: str, reuse: Optional[Dict[str, str]]):
+        if reuse:
+            self._reuse_map[model] = reuse
+        elif model in self._reuse_map:
+            del self._reuse_map[model]
+        self._save_reuse()
+
+
+def load_machine_store(data_dir: Path) -> MachineStore:
+    return MachineStore(data_dir)

+ 546 - 0
backend/app/services/renderer.py

@@ -0,0 +1,546 @@
+"""
+Jinja2 模板渲染服务 - 自动变量识别与条件分支预览版
+从 daping_rec 模板逻辑中提取所有变量,支持条件分支控制预览
+"""
+from __future__ import annotations
+
+import copy
+import random as random_module
+import re
+from typing import Dict, Iterable, List, Optional
+
+from jinja2 import BaseLoader, Environment, StrictUndefined, meta
+from jinja2.exceptions import TemplateError
+
+from fastapi import HTTPException
+
+from ..schemas import (
+    TemplatePreviewRequest,
+    TemplateRecord,
+    TemplateRenderRequest,
+    VariableDefinition,
+)
+
+
+# === 从 daping_rec 模板中识别的完整变量配置 ===
+VARIABLE_DEFAULTS = {
+    # 基础变量
+    'city': '青岛市',
+    'period': '早上',
+    'season': '夏天',
+    
+    # 程序相关
+    'program_id': '1',
+    
+    # 推荐类型 - 条件分支控制变量
+    'recommend_type': 'POPULAR',  # POPULAR / CUSTOM
+    'rerank_reason': '',  # PIN_CONSECUTIVE / PIN_WEEKLY / ""(空=走推荐类型分支)
+    'feature_name': '最喜欢用',
+    
+    # 人气统计
+    'count': 150,
+    'user_count': 50,
+    'user_proportion': 60,
+    
+    # 天气
+    'weather': '小雪',
+    'wd_link': '1',
+    'washdry': '_wd',
+    
+    # 季节
+    'time_term': '立春',
+    'filter_rule_code': 'time_season_in',
+    
+    # 关怀(同时包含洗衣机和干衣机的关怀功能,避免模板找不到键)
+    'care': {
+        'night_wash': {'active': 1, 'display_name': '夜间洗'},  # 洗衣机
+        'night_dry': {'active': 1, 'display_name': '夜间烘'},   # 干衣机
+        'wind_cruise': {'active': 1, 'display_name': '风巡航'}  # 洗衣机/干衣机共用
+    },
+    
+    # 干衣机参数
+    'changed_params_dry': {
+        'DryLevel': '3',
+        'DryTemp': '2',
+        'FanLevel': '2'
+    },
+    
+    # 洗衣机参数
+    'changed_params_wash': {
+        'setTemp': 40,
+        'speed': 12,
+        'spinTime': 3,
+        'washTime': 45
+    },
+    
+    # 简写变量映射(模板中可能直接使用 changed_params)
+    # 合并洗衣机和干衣机参数,避免干衣机模板访问 DryLevel 等键时报错
+    'changed_params': {
+        'setTemp': 40,
+        'speed': 12,
+        'spinTime': 3,
+        'washTime': 45,
+        'DryLevel': '3',
+        'DryTemp': '2',
+        'FanLevel': '2'
+    },
+    
+    # 多卡模板变量
+    'washNight': '1',
+    'WFreeCn': '1',
+}
+
+# === 条件分支变量定义 ===
+CONDITION_VARIABLES = {
+    'rerank_reason': {
+        'label': '推荐原因',
+        'type': 'select',
+        'options': [
+            {'value': 'PIN_CONSECUTIVE', 'label': '连续使用习惯'},
+            {'value': 'PIN_WEEKLY', 'label': '每周习惯'},
+            {'value': '', 'label': '无(走推荐类型分支)'},
+        ],
+        'description': '控制推荐原因分支',
+    },
+    'recommend_type': {
+        'label': '推荐类型',
+        'type': 'select',
+        'options': [
+            {'value': 'POPULAR', 'label': '人气推荐'},
+            {'value': 'CUSTOM', 'label': '定制推荐'},
+        ],
+        'description': '控制推荐类型分支',
+    },
+    'filter_rule_code': {
+        'label': '季节规则',
+        'type': 'select',
+        'options': [
+            {'value': 'time_season_in', 'label': '入季/上新'},
+            {'value': 'time_season_storage', 'label': '收纳'},
+        ],
+        'description': '控制季节模板分支',
+    },
+    'feature_name': {
+        'label': '特征名称',
+        'type': 'select',
+        'options': [
+            {'value': '最喜欢用', 'label': '最喜欢用'},
+            {'value': '最近很喜欢用', 'label': '最近很喜欢用'},
+            {'value': '最近一周常用', 'label': '最近一周常用'},
+            {'value': '上周这个时间喜欢用', 'label': '上周这个时间喜欢用'},
+            {'value': '', 'label': '空(走兜底播报)'},
+        ],
+        'description': '控制特征分支(空字符串走else分支)',
+    },
+    'program_id': {
+        'label': '程序ID',
+        'type': 'text',
+        'placeholder': '1-131',
+        'description': '程序ID(1=棉麻, 15=速洗,  etc)',
+    },
+}
+
+# === 数据变量定义 ===
+DATA_VARIABLES = {
+    'city': {'label': '城市', 'placeholder': '青岛市'},
+    'period': {'label': '时段', 'placeholder': '早上/中午/晚上'},
+    'season': {'label': '季节', 'placeholder': '夏天'},
+    'weather': {'label': '天气', 'placeholder': '小雪'},
+    'count': {'label': '使用次数', 'placeholder': '150'},
+    'user_count': {'label': '用户数', 'placeholder': '50'},
+    'user_proportion': {'label': '用户比例', 'placeholder': '60'},
+    'time_term': {'label': '节气', 'placeholder': '立春'},
+}
+
+
+class TemplateRenderer:
+    """智能模板渲染器 - 支持条件分支预览"""
+
+    def __init__(self) -> None:
+        self.env = Environment(
+            loader=BaseLoader(),
+            undefined=StrictUndefined,
+            trim_blocks=True,
+            lstrip_blocks=True,
+            autoescape=False,
+        )
+        # 注册 random filter - 解决 'str' object is not callable
+        self.env.filters['random'] = self._jinja_random
+        # 注册全局变量
+        self.env.globals['program_name_map'] = self._default_program_name_map()
+
+    def _jinja_random(self, seq):
+        """Jinja2 random filter - 从序列中随机选一个元素"""
+        if not seq:
+            return ""
+        try:
+            return random_module.choice(list(seq))
+        except:
+            return ""
+
+    def _default_program_name_map(self) -> dict:
+        """程序名称映射"""
+        programs = {
+            "1": ("棉麻", ""), "2": ("化纤", ""), "3": ("羽绒", ""), "6": ("衬衣", ""), "7": ("童装", ""),
+            "9": ("烫烫净", ""), "10": ("羊毛", ""), "13": ("筒自洁", "清洁洗衣机专用"), "14": ("内衣", ""),
+            "15": ("速洗", "快速洗涤"), "17": ("大件", ""), "18": ("运动服", ""), "19": ("超柔", ""),
+            "22": ("丝绸", ""), "27": ("漂+脱", ""), "28": ("单脱水", ""), "30": ("除毛烘", ""),
+            "33": ("定时烘", ""), "34": ("静筒烘", ""), "39": ("云程序", ""),
+            "40": ("强力洗", ""), "43": ("定时烘", ""), "45": ("云程序", ""),
+            "52": ("冲锋衣", ""), "56": ("智慧烘", ""), "58": ("浴巾", ""),
+            "61": ("智慧洗", "智能识别衣物"), "79": ("快洗15", ""), "107": ("除菌螨", ""),
+            "118": ("除菌螨", ""), "131": ("夏凉被", ""),
+        }
+
+        class Program:
+            def __init__(self, name, desc=""):
+                self.name = name
+                self.desc = desc
+
+            def __str__(self):
+                return self.name
+
+            def __format__(self, fmt):
+                return self.name.__format__(fmt)
+            
+            def get(self, key, default=None):
+                if key in programs:
+                    return programs[key][0]
+                return default
+
+        return {
+            pid: Program(name, desc) for pid, (name, desc) in programs.items()
+        }
+
+    def _extract_common_support(self, common_body: str) -> str:
+        """
+        从common模板中提取宏定义和列表变量定义
+        返回需要合并到子模板前面的代码片段
+        """
+        import re
+        if not common_body:
+            return ""
+        
+        parts = []
+        
+        # 1. 提取 {% macro name(...) %}...{% endmacro %}
+        # 支持 {%- macro ... -%} 和 {%- endmacro -%} 等 trim 语法变体
+        macro_pattern = r'{%-?\s*macro\s+\w+\s*\([^)]*\)\s*-%?%}[\s\S]*?{%-?\s*endmacro\s*-%?%}'
+        for match in re.finditer(macro_pattern, common_body):
+            parts.append(match.group(0))
+        
+        # 2. 提取 {% set var_name = ["..."] %} 形式的列表变量
+        # 匹配 greeting/assistant1/assistant2/user1/user2 等列表变量
+        list_set_pattern = r'{%\s*set\s+(greeting|assistant1|assistant2|user1|user2|period_list|city_list)\s*=\s*\[[^\]]*\]\s*%}'
+        for match in re.finditer(list_set_pattern, common_body, re.IGNORECASE):
+            parts.append(match.group(0))
+        
+        # 3. 也提取 {% set var_name = ("...") %} 或 {% set var_name = "..." %}
+        # 简单的字符串/数字变量
+        simple_set_pattern = r'{%\s*set\s+(feature_week_name|feature_name_default|default_program)\s*=\s*[^%]+\s*%}'
+        for match in re.finditer(simple_set_pattern, common_body, re.IGNORECASE):
+            parts.append(match.group(0))
+        
+        return '\n\n'.join(parts) if parts else ""
+
+    def _find_macro_calls(self, template_body: str) -> set:
+        """从模板AST中提取被当作函数调用的变量名(即宏调用)"""
+        try:
+            ast = self.env.parse(template_body)
+            macro_calls = set()
+            # 遍历AST查找Call节点
+            import jinja2.nodes as nodes
+            for node in ast.find_all(nodes.Call):
+                if isinstance(node.node, nodes.Name):
+                    macro_calls.add(node.node.name)
+            return macro_calls
+        except:
+            return set()
+
+    def preview(self, payload: TemplatePreviewRequest) -> dict:
+        """
+        预览模板 - 基于真实模板正文渲染
+        自动检测 {% set xxx %} 定义的变量,如果没有对应的 {{ xxx }} 输出则自动添加
+        如果有common_body,自动合并宏定义和列表变量
+        返回:{success, result, error, condition_vars, data_vars, template_body}
+        """
+        try:
+            # 先对原始模板处理 auto_add_output
+            body = self._auto_add_output(payload.template_body)
+            
+            # 合并common模板的宏和变量定义(如果提供)
+            if payload.common_body:
+                body = payload.common_body + '\n\n' + body
+            
+            # 分析变量
+            condition_vars, data_vars = self._analyze_variables(body)
+            
+            # 构建上下文 - 使用默认值 + 用户自定义值
+            context = self._build_context(body, payload.parameters)
+            
+            # 渲染
+            template = self.env.from_string(body)
+            rendered = template.render(**context)
+            
+            # 清理输出
+            paragraphs = [p.strip() for p in rendered.split("\n\n") if p.strip()]
+            result_text = "\n\n".join(paragraphs)
+            
+            return {
+                "success": True,
+                "result": result_text,
+                "error": "",
+                "condition_vars": condition_vars,
+                "data_vars": data_vars,
+                "template_body": payload.template_body,
+                "context_used": {k: str(v)[:100] for k, v in context.items() if k != 'program_name_map'}
+            }
+            
+        except TemplateError as exc:
+            return {
+                "success": False,
+                "result": "",
+                "error": f"模板语法错误: {exc}",
+                "condition_vars": [],
+                "data_vars": [],
+                "template_body": payload.template_body,
+                "context_used": {}
+            }
+        except Exception as exc:
+            import traceback
+            return {
+                "success": False,
+                "result": "",
+                "error": f"渲染失败: {exc}\n{traceback.format_exc()}",
+                "condition_vars": [],
+                "data_vars": [],
+                "template_body": payload.template_body,
+                "context_used": {}
+            }
+
+    def _auto_add_output(self, template_body: str) -> str:
+        """
+        自动检测 {% set xxx %}...{% endset %} 块级定义的变量,
+        如果没有对应的 {{ xxx }} 输出则自动添加。
+        只处理块级set(内容块),不处理赋值型set(如 {% set count = 1 %})
+        """
+        import re
+        body = template_body
+        
+        # 只匹配块级set: {% set name %}...{% endset %}
+        set_block_pattern = r'{%\s*set\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*%}'
+        
+        set_vars = set()
+        for match in re.finditer(set_block_pattern, body):
+            var_name = match.group(1)
+            # 排除宏定义
+            if var_name not in ('macro', 'endmacro'):
+                set_vars.add(var_name)
+        
+        # 过滤掉宏名
+        set_vars = {v for v in set_vars if v not in ('macro', 'endmacro')}
+        
+        # 检查每个块级set变量是否有输出
+        added_outputs = []
+        for var in set_vars:
+            # 检查是否有 {{ var }} 输出
+            output_pattern = r'{{\s*' + var + r'\s*}}'
+            if not re.search(output_pattern, body):
+                added_outputs.append(f'{{{{ {var} }}}}')
+        
+        if added_outputs:
+            body = body.rstrip() + '\n\n' + '\n'.join(added_outputs)
+        
+        return body
+
+    def _analyze_variables(self, template_body: str) -> tuple:
+        """
+        分析模板中的变量,分离条件变量和数据变量
+        返回: (condition_vars, data_vars)
+        """
+        try:
+            ast = self.env.parse(template_body)
+            undeclared = meta.find_undeclared_variables(ast)
+        except:
+            return [], []
+        
+        condition_vars = []
+        data_vars = []
+        
+        for var in sorted(undeclared):
+            if var == 'program_name_map':
+                continue
+            
+            # 检查是否是条件变量
+            if var in CONDITION_VARIABLES:
+                cfg = CONDITION_VARIABLES[var]
+                var_def = {
+                    'name': var,
+                    'label': cfg['label'],
+                    'type': cfg.get('type', 'text'),
+                    'value': str(VARIABLE_DEFAULTS.get(var, '')),
+                    'default': str(VARIABLE_DEFAULTS.get(var, '')),
+                    'options': cfg.get('options', []),
+                    'placeholder': cfg.get('placeholder', ''),
+                    'description': cfg.get('description', ''),
+                }
+                condition_vars.append(var_def)
+            elif var in DATA_VARIABLES:
+                cfg = DATA_VARIABLES[var]
+                var_def = {
+                    'name': var,
+                    'label': cfg['label'],
+                    'type': 'text',
+                    'value': str(VARIABLE_DEFAULTS.get(var, '')),
+                    'default': str(VARIABLE_DEFAULTS.get(var, '')),
+                    'placeholder': cfg.get('placeholder', ''),
+                    'description': cfg.get('description', ''),
+                }
+                data_vars.append(var_def)
+            else:
+                # 未知变量,作为数据变量处理
+                var_def = {
+                    'name': var,
+                    'label': var,
+                    'type': 'text',
+                    'value': str(VARIABLE_DEFAULTS.get(var, '')),
+                    'default': str(VARIABLE_DEFAULTS.get(var, '')),
+                    'placeholder': '',
+                    'description': '',
+                }
+                data_vars.append(var_def)
+        
+        return condition_vars, data_vars
+
+    def _build_context(self, template_body: str, custom_params: Optional[dict] = None) -> dict:
+        """
+        智能构建渲染上下文
+        """
+        context = {}
+        
+        # 提取模板变量
+        try:
+            ast = self.env.parse(template_body)
+            undeclared = meta.find_undeclared_variables(ast)
+        except:
+            undeclared = set()
+        
+        # 检测模板中的宏调用,不为宏名填充空字符串
+        macro_calls = self._find_macro_calls(template_body)
+        
+        # 为每个变量填充默认值
+        for var in undeclared:
+            if var == 'program_name_map':
+                continue
+            
+            # 跳过宏调用(这些应该由模板中的 {% macro %} 定义提供)
+            if var in macro_calls:
+                continue
+            
+            if var in VARIABLE_DEFAULTS:
+                context[var] = copy.deepcopy(VARIABLE_DEFAULTS[var])
+            else:
+                context[var] = ""
+        
+        # 合并自定义参数(覆盖默认值)
+        if custom_params:
+            for key, value in custom_params.items():
+                # 尝试类型转换
+                if key in ('count', 'user_count', 'user_proportion'):
+                    try:
+                        context[key] = int(value)
+                    except:
+                        context[key] = value
+                elif key in ('changed_params', 'changed_params_wash', 'changed_params_dry', 'care'):
+                    # 如果是JSON字符串,先解析为对象
+                    if isinstance(value, str):
+                        import json
+                        try:
+                            context[key] = json.loads(value)
+                        except (json.JSONDecodeError, ValueError):
+                            # 解析失败,使用默认值
+                            context[key] = copy.deepcopy(VARIABLE_DEFAULTS.get(key, {}))
+                    else:
+                        context[key] = value
+                else:
+                    context[key] = value
+        
+        return context
+
+    def render_from_record(self, record: TemplateRecord, parameters: dict) -> str:
+        """渲染模板记录(保留旧接口兼容)"""
+        normalized = self._normalize_parameters(parameters, record.input_variables)
+        normalized = self._fill_missing_variables(record.template_body, normalized)
+        return self._render(record.template_body, normalized)
+
+    def _fill_missing_variables(self, template_body: str, parameters: dict) -> dict:
+        """扫描模板,为未提供的变量填充默认值"""
+        try:
+            ast = self.env.parse(template_body)
+            undeclared = meta.find_undeclared_variables(ast)
+            for var in undeclared:
+                if var not in parameters and var != 'program_name_map':
+                    parameters[var] = copy.deepcopy(VARIABLE_DEFAULTS.get(var, ""))
+        except Exception:
+            pass
+        return parameters
+
+    def _render(self, template_body: str, parameters: dict) -> str:
+        """执行渲染"""
+        try:
+            template = self.env.from_string(template_body)
+            rendered = template.render(**parameters)
+            paragraphs = [p.strip() for p in rendered.split("\n\n") if p.strip()]
+            return "\n\n".join(paragraphs)
+        except TemplateError as exc:
+            raise HTTPException(status_code=400, detail=f"模板渲染出错: {exc}") from exc
+
+    def _normalize_parameters(
+        self,
+        parameters: Dict[str, object],
+        definitions: Optional[Iterable[VariableDefinition]] = None,
+    ) -> Dict[str, object]:
+        """参数类型转换"""
+        type_map = {}
+        for definition in definitions or []:
+            if isinstance(definition, VariableDefinition):
+                name = (definition.name or "").strip()
+                data_type = (definition.data_type or "").lower()
+            elif isinstance(definition, dict):
+                name = (definition.get("name") or "").strip()
+                data_type = (definition.get("data_type") or "").lower()
+            else:
+                continue
+            if name:
+                type_map[name] = data_type
+        return {
+            key: self._convert_value(value, type_map.get(key))
+            for key, value in parameters.items()
+        }
+
+    def _convert_value(self, value: object, data_type: Optional[str]) -> object:
+        """值类型转换"""
+        if value is None or isinstance(value, (int, float, bool)):
+            return value
+        if isinstance(value, str):
+            stripped = value.strip()
+            if stripped == "":
+                return None
+            if (data_type or "").startswith("bool"):
+                lowered = stripped.lower()
+                if lowered in {"true", "1", "yes", "y", "on"}:
+                    return True
+                if lowered in {"false", "0", "no", "n", "off"}:
+                    return False
+            if (data_type or "") in {"number", "integer"} or self._looks_numeric(stripped):
+                try:
+                    return int(stripped) if stripped.isdigit() else float(stripped)
+                except ValueError:
+                    return value
+        return value
+
+    def _looks_numeric(self, value: str) -> bool:
+        return bool(re.fullmatch(r"[+-]?\d+(\.\d+)?", value))
+
+
+renderer = TemplateRenderer()

+ 179 - 0
backend/app/services/template_store.py

@@ -0,0 +1,179 @@
+from __future__ import annotations
+
+import json
+from pathlib import Path
+from typing import Dict, List, Optional
+
+from fastapi import HTTPException
+
+from ..schemas import TemplateCreate, TemplateRecord, TemplateUpdate
+
+
+class TemplateStore:
+    """Simple JSON-file based store for template definitions."""
+
+    def __init__(self, data_path: Path):
+        self.data_path = data_path
+        self.data_path.parent.mkdir(parents=True, exist_ok=True)
+        if not self.data_path.exists():
+            self.data_path.write_text(json.dumps([], ensure_ascii=False, indent=2))
+        self._cache: Dict[str, TemplateRecord] = {}
+        self._load()
+
+    def _load(self) -> None:
+        raw = json.loads(self.data_path.read_text(encoding='utf-8'))
+        self._cache = {item["id"]: TemplateRecord(**item) for item in raw}
+
+    def _persist(self) -> None:
+        # Use JSON mode so datetime fields are converted to ISO strings.
+        serializable = [record.model_dump(mode="json") for record in self._cache.values()]
+        self.data_path.write_text(
+            json.dumps(serializable, ensure_ascii=False, indent=2),
+            encoding='utf-8'
+        )
+
+    def list_templates(self, machine_model: str = None, device_type: str = None, template_type: str = None) -> List[TemplateRecord]:
+        templates = list(self._cache.values())
+        if machine_model:
+            templates = [t for t in templates if t.machine_model == machine_model]
+        if device_type:
+            templates = [t for t in templates if t.device_type == device_type]
+        if template_type:
+            templates = [t for t in templates if t.template_type == template_type]
+        return templates
+
+    def get_template(self, template_id: str) -> TemplateRecord:
+        try:
+            record = self._cache[template_id]
+            # 如果是复用模板,返回源模板的内容(但保留当前模板的元数据)
+            if record.reusable_from:
+                source = self._cache.get(record.reusable_from)
+                if source:
+                    # 返回源模板,但保留当前模板的元数据
+                    merged = source.model_copy()
+                    merged.id = record.id
+                    merged.name = record.name
+                    merged.category = record.category
+                    merged.device_type = record.device_type
+                    merged.machine_model = record.machine_model
+                    merged.template_type = record.template_type
+                    merged.type_id = record.type_id
+                    merged.zone_ids = record.zone_ids
+                    merged.card_type = record.card_type
+                    merged.reusable_from = record.reusable_from
+                    merged.reuse_targets = record.reuse_targets
+                    merged.description = record.description
+                    merged.created_at = record.created_at
+                    merged.updated_at = record.updated_at
+                    return merged
+            return record
+        except KeyError as exc:
+            raise HTTPException(status_code=404, detail="模板不存在") from exc
+
+    def create_template(self, payload: TemplateCreate) -> TemplateRecord:
+        record = TemplateRecord(**payload.model_dump())
+        self._cache[record.id] = record
+        self._persist()
+        return record
+
+    def update_template(self, template_id: str, payload: TemplateUpdate) -> TemplateRecord:
+        record = self.get_template(template_id)
+        update_data = payload.model_dump(exclude_none=True)
+        updated = record.model_copy(update=update_data)
+        from datetime import datetime
+
+        updated.updated_at = datetime.utcnow()
+        self._cache[template_id] = updated
+        self._persist()
+        return updated
+
+    def delete_template(self, template_id: str) -> None:
+        if template_id not in self._cache:
+            raise HTTPException(status_code=404, detail="模板不存在")
+        record = self._cache[template_id]
+        # 如果该模板被其他模板复用,禁止删除
+        if record.reuse_targets:
+            raise HTTPException(status_code=400, detail=f"该模板被 {len(record.reuse_targets)} 个模板复用,无法删除")
+        # 如果这是复用模板,需要从源模板的 reuse_targets 中移除
+        if record.reusable_from and record.reusable_from in self._cache:
+            source = self._cache[record.reusable_from]
+            if template_id in source.reuse_targets:
+                source.reuse_targets.remove(template_id)
+        del self._cache[template_id]
+        self._persist()
+
+    def generate_redis_keys(self, record: TemplateRecord) -> dict:
+        """根据模板信息自动生成 Redis 一级 key 列表"""
+        if not record.type_id or not record.zone_ids:
+            return {"second": record.redis_key_second or "", "first": []}
+
+        # 二级 key
+        card = record.card_type or "single_card"
+        second_key = f"broadcast-template-jinja:{card}:{record.device_type}:{record.template_type}"
+
+        # 模板类型到 card_type 和 recommend_type 的映射
+        template_type_map = {
+            "common": ("common", None),
+            "time_popular": ("time_made_popularity", "1"),
+            "time_custom": ("time_made_popularity", "2"),
+            "time_season": ("time_season_storage", None),  # 同时有 time_season_in
+            "time_care": ("time_care", None),
+            "time_environmental": ("time_environmental", None),
+            "time_weather": ("time_weather", None),
+            "time_health": ("time_health", None),
+        }
+
+        card_type_key, recommend_type = template_type_map.get(record.template_type, (record.template_type, None))
+
+        first_keys = []
+        for zone_id in record.zone_ids:
+            if record.template_type == "common":
+                key = f"broadcast-template-jinja:{record.type_id}:{zone_id}:common"
+                first_keys.append(key)
+            else:
+                # 非 common 模板,需要为每个 zone_id 和 recommend_type 生成 key
+                if recommend_type:
+                    key = f"broadcast-template-jinja:{record.type_id}:{zone_id}:{card_type_key}:{recommend_type}"
+                    first_keys.append(key)
+                else:
+                    # 对于 season,同时生成 storage 和 in
+                    if record.template_type == "time_season":
+                        first_keys.append(f"broadcast-template-jinja:{record.type_id}:{zone_id}:time_season_storage:1")
+                        first_keys.append(f"broadcast-template-jinja:{record.type_id}:{zone_id}:time_season_in:1")
+                    else:
+                        key = f"broadcast-template-jinja:{record.type_id}:{zone_id}:{card_type_key}:1"
+                        first_keys.append(key)
+
+        return {"second": second_key, "first": first_keys}
+
+    def list_categories(self, machine_model: str = None) -> List[str]:
+        templates = self._cache.values()
+        if machine_model:
+            templates = [t for t in templates if t.machine_model == machine_model]
+        categories = {record.category for record in templates}
+        return sorted(categories)
+
+    def list_machine_models(self) -> List[str]:
+        models = {record.machine_model for record in self._cache.values()}
+        return sorted(models)
+
+    def list_device_types(self, machine_model: str = None) -> List[str]:
+        templates = self._cache.values()
+        if machine_model:
+            templates = [t for t in templates if t.machine_model == machine_model]
+        device_types = {record.device_type for record in templates}
+        return sorted(device_types)
+
+    def list_template_types(self, machine_model: str = None, device_type: str = None) -> List[str]:
+        templates = self._cache.values()
+        if machine_model:
+            templates = [t for t in templates if t.machine_model == machine_model]
+        if device_type:
+            templates = [t for t in templates if t.device_type == device_type]
+        template_types = {record.template_type for record in templates}
+        return sorted(template_types)
+
+
+def load_store(base_dir: Path) -> TemplateStore:
+    data_path = base_dir / "data" / "templates.json"
+    return TemplateStore(data_path)

+ 64 - 0
backend/data/machine_configs.json

@@ -0,0 +1,64 @@
+{
+  "x11": {
+    "label": "X11",
+    "type_ids": {
+      "wash": "201c51890c31c308050100218008390000000000000000000000000000000040",
+      "dry": "201c51890c31c308310100218003484748000000000000000000000000000040"
+    },
+    "zone_ids": {"wash": ["2", "3"], "dry": ["0"]},
+    "card_type": "single_card"
+  },
+  "hplus": {
+    "label": "Hplus",
+    "type_ids": {
+      "wash": "201c51890c31c308050400218003484843000001000000000000000000000040",
+      "dry": "201c51890c31c308050400218003484843000001000000000000000000000040"
+    },
+    "zone_ids": {"wash": ["3"], "dry": ["0"]},
+    "card_type": "single_card"
+  },
+  "ultra": {
+    "label": "Ultra",
+    "type_ids": {
+      "wash": "201c51890c31c308050100218008054150000000000000000000000000000040",
+      "dry": "201c51890c31c308050100218008054150000000000000000000000000000040"
+    },
+    "zone_ids": {"wash": ["1", "3", "41", "43", "61", "63"], "dry": ["1", "3", "41", "43", "61", "63"]},
+    "card_type": "single_card"
+  },
+  "f2_multi": {
+    "label": "F2 Multi",
+    "type_ids": {
+      "wash": "201c51890c31c30805040021800"
+    },
+    "zone_ids": {"wash": ["1"]},
+    "card_type": "multi_card"
+  },
+  "seeker": {
+    "label": "Seeker",
+    "type_ids": {
+      "wash": "201c51890c31c308050100218008054251410000000000000000000000000040",
+      "dry": "201c51890c31c308310100218008054251410003000000000000000000000040"
+    },
+    "zone_ids": {"wash": ["2", "3", "4"], "dry": ["0"]},
+    "card_type": "single_card"
+  },
+  "seekerR2": {
+    "label": "SeekerR2",
+    "type_ids": {
+      "wash": "201c51890c31c308050100218008054146000000000000000000000000000040",
+      "dry": "201c51890c31c308310100218008054146000000000000000000000000000040"
+    },
+    "zone_ids": {"wash": ["1", "41", "61"], "dry": ["24"]},
+    "card_type": "single_card"
+  },
+  "x11_ultra": {
+    "label": "X11 Ultra",
+    "type_ids": {
+      "wash": "201c51890c31c308050400218003484446000000000000000000000000000040",
+      "dry": "201c51890c31c308050400218003484446000000000000000000000000000040"
+    },
+    "zone_ids": {"wash": ["1", "7"], "dry": ["24"]},
+    "card_type": "single_card"
+  }
+}

+ 6 - 0
backend/data/reuse_map.json

@@ -0,0 +1,6 @@
+{
+  "ultra": {"wash": "wash", "dry": "dry"},
+  "seeker": {"wash": "wash", "dry": "dry"},
+  "seekerR2": {"wash": "wash", "dry": "dry"},
+  "x11_ultra": {"wash": "wash", "dry": "dry"}
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 26 - 0
backend/data/templates.json


+ 5 - 0
backend/requirements.txt

@@ -0,0 +1,5 @@
+fastapi==0.115.5
+uvicorn[standard]==0.32.0
+jinja2==3.1.4
+pydantic==2.9.2
+python-multipart==0.0.10

+ 17 - 0
check_api.py

@@ -0,0 +1,17 @@
+import urllib.request
+import json
+
+try:
+    req = urllib.request.Request('http://localhost:8000/api/templates')
+    with urllib.request.urlopen(req, timeout=5) as resp:
+        data = json.loads(resp.read().decode('utf-8'))
+        print(f'总模板数: {len(data)}')
+        models = sorted(set(t['machine_model'] for t in data))
+        print(f'机型: {models}')
+        types = sorted(set(t['template_type'] for t in data))
+        print(f'模板类型: {types}')
+        # 打印前5个模板
+        for t in data[:5]:
+            print(f"  - {t['id']}: {t['name']}")
+except Exception as e:
+    print(f'Error: {e}')

+ 9 - 0
check_api_data.py

@@ -0,0 +1,9 @@
+import requests
+
+r = requests.get('http://localhost:8000/api/templates')
+data = r.json()
+print(f'总数: {len(data)}')
+if data:
+    print(f'字段: {list(data[0].keys())}')
+    print(f'示例: {data[0]["id"]}, {data[0]["machine_model"]}, {data[0]["template_type"]}')
+    print(f'有second_key字段: {"second_key" in data[0]}')

+ 50 - 0
check_real_template.py

@@ -0,0 +1,50 @@
+"""检查实际模板内容和预览结果"""
+import requests
+
+API_BASE = "http://localhost:8000/api"
+
+# 获取所有模板
+response = requests.get(f"{API_BASE}/templates")
+templates = response.json()
+
+# 找一个有template_body的原创模板
+template = None
+for tpl in templates:
+    if tpl.get('template_body') and not tpl.get('reusable_from'):
+        template = tpl
+        break
+
+if not template:
+    print("❌ 没有找到原创模板")
+    exit(1)
+
+print(f"✅ 模板: {template['machine_model']}/{template['template_type']}")
+print(f"   ID: {template['id'][:12]}...")
+print()
+
+print("=" * 80)
+print("📝 模板正文(前500字符):")
+print("=" * 80)
+print(template['template_body'][:500])
+print()
+
+# 预览
+print("=" * 80)
+print("🔍 预览结果:")
+print("=" * 80)
+response = requests.post(f"{API_BASE}/templates/preview", json={
+    "template_body": template['template_body']
+})
+result = response.json()
+
+if result.get('success'):
+    print(result.get('result', ''))
+else:
+    print(f"❌ 错误: {result.get('error')}")
+
+print()
+print("=" * 80)
+print("📊 变量列表:")
+print("=" * 80)
+for v in result.get('variables', []):
+    print(f"  {v['name']}: {v['value']}")

+ 15 - 0
check_templates.py

@@ -0,0 +1,15 @@
+import urllib.request
+import json
+
+resp = urllib.request.urlopen('http://localhost:8000/api/templates')
+data = json.loads(resp.read())
+print('总数:', len(data))
+print('x11:', len([t for t in data if t['machine_model']=='x11']))
+print('hplus:', len([t for t in data if t['machine_model']=='hplus']))
+print('f2_multi:', len([t for t in data if t['machine_model']=='f2_multi']))
+print('ultra:', len([t for t in data if t['machine_model']=='ultra']))
+print('seeker:', len([t for t in data if t['machine_model']=='seeker']))
+print('x11_ultra:', len([t for t in data if t['machine_model']=='x11_ultra']))
+print('\n前3个模板ID:')
+for t in data[:3]:
+    print(f"  {t['id']}: {t['name']}")

+ 30 - 0
check_templates_body.py

@@ -0,0 +1,30 @@
+"""检查模板数据"""
+import requests
+
+API_BASE = "http://localhost:8000/api"
+
+response = requests.get(f"{API_BASE}/templates")
+templates = response.json()
+
+print(f"总模板数: {len(templates)}")
+print()
+
+# 统计
+with_body = 0
+without_body = 0
+
+for tpl in templates[:10]:
+    has_body = bool(tpl.get('template_body'))
+    if has_body:
+        with_body += 1
+        print(f"✅ {tpl['machine_model']}/{tpl['template_type']}")
+        print(f"   body长度: {len(tpl['template_body'])}")
+        print(f"   body前100字符: {tpl['template_body'][:100]}")
+    else:
+        without_body += 1
+        print(f"❌ {tpl['machine_model']}/{tpl['template_type']} - 无template_body")
+        if tpl.get('reusable_from'):
+            print(f"   复用来源: {tpl['reusable_from']}")
+    print()
+
+print(f"\n前10个中: {with_body} 有body, {without_body} 无body")

+ 24 - 0
fix_season_random.py

@@ -0,0 +1,24 @@
+import json
+
+with open('backend/data/templates.json', 'r', encoding='utf-8') as f:
+    data = json.load(f)
+
+# 找出所有季节模板
+season_templates = [t for t in data if 'season' in t.get('template_type', '')]
+
+print(f'找到 {len(season_templates)} 个季节模板:')
+for t in season_templates:
+    has_first = '| first' in t.get('template_body', '')
+    has_random = '| random' in t.get('template_body', '')
+    print(f"  {t['id']}: {t['template_type']} (first={has_first}, random={has_random})")
+    
+    # 替换 first 为 random
+    if has_first:
+        t['template_body'] = t['template_body'].replace('| first}}', '| random}}')
+        print(f"    ✓ 已替换 | first 为 | random")
+
+# 保存
+with open('backend/data/templates.json', 'w', encoding='utf-8') as f:
+    json.dump(data, f, ensure_ascii=False, indent=2)
+
+print(f'\n完成!已保存修改')

+ 18 - 0
fix_templates.py

@@ -0,0 +1,18 @@
+import json
+
+with open('d:/00_26code/x11_template_vue/backend/data/templates.json', 'r', encoding='utf-8') as f:
+    data = json.load(f)
+
+count = 0
+for item in data:
+    if 'template_body' in item:
+        body = item['template_body']
+        if ']| first}}' in body:
+            item['template_body'] = body.replace(']| first}}', ']| random}}')
+            count += 1
+            print(f"Modified: {item.get('id', 'unknown')}")
+
+with open('d:/00_26code/x11_template_vue/backend/data/templates.json', 'w', encoding='utf-8') as f:
+    json.dump(data, f, ensure_ascii=False, indent=2)
+
+print(f'Total modified: {count}')

+ 24 - 0
frontend/.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
frontend/.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar"]
+}

+ 5 - 0
frontend/README.md

@@ -0,0 +1,5 @@
+# Vue 3 + Vite
+
+This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).

+ 13 - 0
frontend/index.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>frontend</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 1150 - 0
frontend/package-lock.json

@@ -0,0 +1,1150 @@
+{
+  "name": "frontend",
+  "version": "0.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "frontend",
+      "version": "0.0.0",
+      "dependencies": {
+        "vue": "^3.5.24"
+      },
+      "devDependencies": {
+        "@vitejs/plugin-vue": "^4.6.2",
+        "vite": "^4.5.14"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+      "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+      "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz",
+      "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+      "dependencies": {
+        "@babel/types": "^7.29.8"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz",
+      "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+      "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+      "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+      "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+      "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+      "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+      "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+      "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+      "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+      "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+      "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+      "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+      "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+      "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+      "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+      "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+      "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+      "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+      "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+      "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+      "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+      "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+      "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
+      "dev": true,
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.0.0 || ^5.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.42.tgz",
+      "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==",
+      "dependencies": {
+        "@babel/parser": "^7.29.8",
+        "@vue/shared": "3.5.42",
+        "entities": "^7.0.1",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz",
+      "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
+      "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
+      "dependencies": {
+        "@babel/parser": "^7.29.8",
+        "@vue/compiler-core": "3.5.42",
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/compiler-ssr": "3.5.42",
+        "@vue/shared": "3.5.42",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.19",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz",
+      "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.42.tgz",
+      "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==",
+      "dependencies": {
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.42.tgz",
+      "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz",
+      "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.42",
+        "@vue/runtime-core": "3.5.42",
+        "@vue/shared": "3.5.42",
+        "csstype": "^3.2.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.42.tgz",
+      "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.42",
+        "@vue/runtime-dom": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.42.tgz",
+      "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g=="
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
+    },
+    "node_modules/entities": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
+      "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.20.tgz",
+      "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.18.20",
+        "@esbuild/android-arm64": "0.18.20",
+        "@esbuild/android-x64": "0.18.20",
+        "@esbuild/darwin-arm64": "0.18.20",
+        "@esbuild/darwin-x64": "0.18.20",
+        "@esbuild/freebsd-arm64": "0.18.20",
+        "@esbuild/freebsd-x64": "0.18.20",
+        "@esbuild/linux-arm": "0.18.20",
+        "@esbuild/linux-arm64": "0.18.20",
+        "@esbuild/linux-ia32": "0.18.20",
+        "@esbuild/linux-loong64": "0.18.20",
+        "@esbuild/linux-mips64el": "0.18.20",
+        "@esbuild/linux-ppc64": "0.18.20",
+        "@esbuild/linux-riscv64": "0.18.20",
+        "@esbuild/linux-s390x": "0.18.20",
+        "@esbuild/linux-x64": "0.18.20",
+        "@esbuild/netbsd-x64": "0.18.20",
+        "@esbuild/openbsd-x64": "0.18.20",
+        "@esbuild/sunos-x64": "0.18.20",
+        "@esbuild/win32-arm64": "0.18.20",
+        "@esbuild/win32-ia32": "0.18.20",
+        "@esbuild/win32-x64": "0.18.20"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.18",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz",
+      "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+    },
+    "node_modules/postcss": {
+      "version": "8.5.28",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.28.tgz",
+      "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.18",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "3.30.0",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.30.0.tgz",
+      "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==",
+      "dev": true,
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=14.18.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/vite": {
+      "version": "4.5.14",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-4.5.14.tgz",
+      "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.18.10",
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      },
+      "peerDependencies": {
+        "@types/node": ">= 14",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.42.tgz",
+      "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/compiler-sfc": "3.5.42",
+        "@vue/runtime-dom": "3.5.42",
+        "@vue/server-renderer": "3.5.42",
+        "@vue/shared": "3.5.42"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    }
+  },
+  "dependencies": {
+    "@babel/helper-string-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+      "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+      "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="
+    },
+    "@babel/parser": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz",
+      "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+      "requires": {
+        "@babel/types": "^7.29.8"
+      }
+    },
+    "@babel/types": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz",
+      "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+      "requires": {
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
+      }
+    },
+    "@esbuild/android-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+      "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+      "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+      "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+      "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+      "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+      "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+      "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+      "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+      "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+      "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-loong64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+      "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-mips64el": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+      "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ppc64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+      "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-riscv64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+      "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-s390x": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+      "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+      "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/netbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/openbsd-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+      "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/sunos-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+      "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-arm64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+      "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-ia32": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+      "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+      "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+      "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="
+    },
+    "@vitejs/plugin-vue": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+      "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
+      "dev": true,
+      "requires": {}
+    },
+    "@vue/compiler-core": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.42.tgz",
+      "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==",
+      "requires": {
+        "@babel/parser": "^7.29.8",
+        "@vue/shared": "3.5.42",
+        "entities": "^7.0.1",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz",
+      "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==",
+      "requires": {
+        "@vue/compiler-core": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
+      "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
+      "requires": {
+        "@babel/parser": "^7.29.8",
+        "@vue/compiler-core": "3.5.42",
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/compiler-ssr": "3.5.42",
+        "@vue/shared": "3.5.42",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.19",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz",
+      "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==",
+      "requires": {
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "@vue/reactivity": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.42.tgz",
+      "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==",
+      "requires": {
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.42.tgz",
+      "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==",
+      "requires": {
+        "@vue/reactivity": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz",
+      "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==",
+      "requires": {
+        "@vue/reactivity": "3.5.42",
+        "@vue/runtime-core": "3.5.42",
+        "@vue/shared": "3.5.42",
+        "csstype": "^3.2.3"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.42.tgz",
+      "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==",
+      "requires": {
+        "@vue/compiler-ssr": "3.5.42",
+        "@vue/runtime-dom": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.42.tgz",
+      "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g=="
+    },
+    "csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
+    },
+    "entities": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
+      "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="
+    },
+    "esbuild": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.20.tgz",
+      "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+      "dev": true,
+      "requires": {
+        "@esbuild/android-arm": "0.18.20",
+        "@esbuild/android-arm64": "0.18.20",
+        "@esbuild/android-x64": "0.18.20",
+        "@esbuild/darwin-arm64": "0.18.20",
+        "@esbuild/darwin-x64": "0.18.20",
+        "@esbuild/freebsd-arm64": "0.18.20",
+        "@esbuild/freebsd-x64": "0.18.20",
+        "@esbuild/linux-arm": "0.18.20",
+        "@esbuild/linux-arm64": "0.18.20",
+        "@esbuild/linux-ia32": "0.18.20",
+        "@esbuild/linux-loong64": "0.18.20",
+        "@esbuild/linux-mips64el": "0.18.20",
+        "@esbuild/linux-ppc64": "0.18.20",
+        "@esbuild/linux-riscv64": "0.18.20",
+        "@esbuild/linux-s390x": "0.18.20",
+        "@esbuild/linux-x64": "0.18.20",
+        "@esbuild/netbsd-x64": "0.18.20",
+        "@esbuild/openbsd-x64": "0.18.20",
+        "@esbuild/sunos-x64": "0.18.20",
+        "@esbuild/win32-arm64": "0.18.20",
+        "@esbuild/win32-ia32": "0.18.20",
+        "@esbuild/win32-x64": "0.18.20"
+      }
+    },
+    "estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "optional": true
+    },
+    "magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "requires": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "nanoid": {
+      "version": "3.3.18",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz",
+      "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="
+    },
+    "picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+    },
+    "postcss": {
+      "version": "8.5.28",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.28.tgz",
+      "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
+      "requires": {
+        "nanoid": "^3.3.18",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "rollup": {
+      "version": "3.30.0",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.30.0.tgz",
+      "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==",
+      "dev": true,
+      "requires": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
+    },
+    "vite": {
+      "version": "4.5.14",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-4.5.14.tgz",
+      "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
+      "dev": true,
+      "requires": {
+        "esbuild": "^0.18.10",
+        "fsevents": "~2.3.2",
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
+      }
+    },
+    "vue": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.42.tgz",
+      "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==",
+      "requires": {
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/compiler-sfc": "3.5.42",
+        "@vue/runtime-dom": "3.5.42",
+        "@vue/server-renderer": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    }
+  }
+}

+ 18 - 0
frontend/package.json

@@ -0,0 +1,18 @@
+{
+  "name": "frontend",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "vue": "^3.5.24"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^4.6.2",
+    "vite": "^4.5.14"
+  }
+}

+ 1 - 0
frontend/public/vite.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

+ 1749 - 0
frontend/src/App.vue

@@ -0,0 +1,1749 @@
+<template>
+  <div class="admin-layout">
+    <!-- 左侧蓝色导航 -->
+    <aside class="sidebar" :class="{ collapsed: sidebarCollapsed }">
+      <div class="sidebar-header" @click="toggleSidebar">
+        <h2 v-if="!sidebarCollapsed">🎯 模板管理</h2>
+        <h2 v-else>🎯</h2>
+      </div>
+      
+      <nav class="sidebar-nav">
+        <div class="nav-section">
+          <div class="nav-title" v-if="!sidebarCollapsed">机型筛选</div>
+          <div class="nav-item" 
+               :class="{ active: !filters.machine_model }"
+               @click="filters.machine_model = null; loadTemplates()">
+            <span class="icon">📋</span>
+            <span v-if="!sidebarCollapsed">全部机型</span>
+          </div>
+          <div class="nav-item"
+               v-for="model in machineModels"
+               :key="model"
+               :class="{ active: filters.machine_model === model }"
+               @click="filters.machine_model = model; loadTemplates()">
+            <span class="icon">{{ getModelIcon(model) }}</span>
+            <span v-if="!sidebarCollapsed">{{ model }}</span>
+          </div>
+        </div>
+
+        <div class="nav-section" v-if="filters.machine_model">
+          <div class="nav-title" v-if="!sidebarCollapsed">设备类型</div>
+          <div class="nav-item"
+               :class="{ active: !filters.device_type }"
+               @click="filters.device_type = null; loadTemplates()">
+            <span class="icon">🔧</span>
+            <span v-if="!sidebarCollapsed">全部类型</span>
+          </div>
+          <div class="nav-item"
+               v-for="type in deviceTypes"
+               :key="type"
+               :class="{ active: filters.device_type === type }"
+               @click="filters.device_type = type; loadTemplates()">
+            <span class="icon">{{ type === 'wash' ? '👕' : '🌀' }}</span>
+            <span v-if="!sidebarCollapsed">{{ type === 'wash' ? '洗衣机' : '干衣机' }}</span>
+          </div>
+        </div>
+      </nav>
+    </aside>
+
+    <!-- 主内容区 -->
+    <main class="main-content">
+      <!-- 顶部操作栏 -->
+      <header class="top-bar">
+        <h1>语音播报模板管理系统</h1>
+        <div class="actions">
+          <button class="btn btn-secondary" @click="showMachineDialog">
+            ⚙ 机型管理
+          </button>
+          <button class="btn btn-primary" @click="showAddDialog">
+            ➕ 新增模板
+          </button>
+        </div>
+      </header>
+
+      <!-- 模板列表 -->
+      <div class="content-area">
+        <table class="template-table">
+          <thead>
+            <tr>
+              <th>ID</th>
+              <th>机型</th>
+              <th>设备</th>
+              <th>类型</th>
+              <th>二级Key</th>
+              <th>状态</th>
+              <th>操作</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="tpl in templates" :key="tpl.id">
+              <td>{{ tpl.id.slice(0, 8) }}...</td>
+              <td>{{ tpl.machine_model }}</td>
+              <td>{{ tpl.device_type === 'wash' ? '洗衣机' : '干衣机' }}</td>
+              <td>{{ tpl.template_type }}</td>
+              <td class="key-cell" :title="tpl.redis_key_second">{{ tpl.redis_key_second }}</td>
+              <td>
+                <span class="badge" :class="tpl.reusable_from ? 'badge-reuse' : 'badge-original'">
+                  {{ tpl.reusable_from ? '复用' : '原创' }}
+                </span>
+              </td>
+              <td class="action-cell">
+                <button class="btn-sm btn-preview" @click="previewTemplate(tpl)">👁 预览</button>
+                <button class="btn-sm btn-edit" @click="editTemplate(tpl)">✏ 编辑</button>
+                <button class="btn-sm btn-delete" @click="deleteTemplate(tpl)">🗑 删除</button>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+
+        <div v-if="templates.length === 0" class="empty-state">
+          <p>暂无模板数据</p>
+        </div>
+      </div>
+    </main>
+
+    <!-- 预览弹窗 -->
+    <div class="modal-overlay" v-if="showPreview" @click.self="showPreview = false">
+      <div class="modal modal-preview">
+        <div class="modal-header">
+          <h3>🔍 模板预览 - 播报语生成</h3>
+          <button class="btn-close" @click="showPreview = false">×</button>
+        </div>
+        <div class="modal-body">
+          <div class="preview-info">
+            <div class="info-row">
+              <label>模板:</label>
+              <span>{{ previewData.template?.machine_model }} / {{ previewData.template?.template_type }}</span>
+            </div>
+            <div class="info-row" v-if="previewData.context_used">
+              <label>使用变量:</label>
+              <span class="context-tags">
+                <span class="tag" v-for="(val, key) in previewData.context_used" :key="key">{{ key }}={{ val }}</span>
+              </span>
+            </div>
+          </div>
+        
+          <!-- 条件分支控制 -->
+          <div class="variables-section condition-section" v-if="previewData.condition_vars && previewData.condition_vars.length > 0">
+            <h4>🎛 条件分支控制(决定走哪个分支)</h4>
+            <div class="variables-grid">
+              <div class="variable-item" v-for="v in previewData.condition_vars" :key="v.name">
+                <label>{{ v.label }} <span class="var-badge">条件</span></label>
+                <!-- 下拉选择 -->
+                <select v-if="v.type === 'select'" v-model="v.value" @change="onVariableChange">
+                  <option v-for="opt in v.options" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
+                </select>
+                <!-- 文本输入 -->
+                <input v-else v-model="v.value" :placeholder="v.placeholder || v.default || ''" @input="onVariableChange" />
+                <div class="var-desc" v-if="v.description">{{ v.description }}</div>
+              </div>
+            </div>
+          </div>
+        
+          <!-- 数据变量 -->
+          <div class="variables-section data-section" v-if="previewData.data_vars && previewData.data_vars.length > 0">
+            <h4>📊 数据变量</h4>
+            <div class="variables-grid">
+              <div class="variable-item" v-for="v in previewData.data_vars" :key="v.name">
+                <label>{{ v.label }}</label>
+                <input v-model="v.value" :placeholder="v.placeholder || v.default || ''" @input="onVariableChange" />
+                <div class="var-desc" v-if="v.description">{{ v.description }}</div>
+              </div>
+            </div>
+          </div>
+        
+          <!-- 播报语结果 -->
+          <div class="preview-result-section">
+            <div class="result-header">
+              <h4>📢 生成的播报语</h4>
+              <button class="btn btn-sm btn-regen" @click="regeneratePreview" :disabled="previewing">
+                {{ previewing ? '⏳ 生成中...' : '🔄 重新生成(随机换一条)' }}
+              </button>
+            </div>
+                    
+            <div class="result-content" v-if="previewData.result">
+              <div class="result-text">{{ previewData.result }}</div>
+            </div>
+                    
+            <div class="result-empty" v-else-if="!previewData.error">
+              <p>点击"重新生成"按钮生成播报语</p>
+            </div>
+          </div>
+    
+          <!-- 模板正文(折叠,供检查) -->
+          <details class="template-body-section" v-if="previewData.template_body">
+            <summary>📝 查看模板正文</summary>
+            <div class="template-body-content">
+              <pre>{{ previewData.template_body }}</pre>
+            </div>
+          </details>
+        
+          <!-- 错误信息 -->
+          <div class="preview-error" v-if="previewData.error">
+            <h4>❌ 错误信息</h4>
+            <pre>{{ previewData.error }}</pre>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 新增/编辑弹窗 -->
+    <div class="modal-overlay" v-if="showAdd" @click.self="showAdd = false">
+      <div class="modal modal-add">
+        <div class="modal-header">
+          <h3>{{ isEditing ? '✏ 编辑模板' : '➕ 新增模板' }}</h3>
+          <button class="btn-close" @click="showAdd = false">×</button>
+        </div>
+        <div class="modal-body">
+          <!-- 编辑模式:显示完整信息,不可切换模式 -->
+          <div v-if="isEditing" class="form-section edit-mode">
+            <div class="form-group">
+              <label>模板ID</label>
+              <input v-model="formData.id" disabled class="disabled-input" />
+              <small class="help-text">模板ID不可修改</small>
+            </div>
+
+            <div class="form-group">
+              <label>机型标识</label>
+              <input v-model="formData.machine_model" disabled class="disabled-input" />
+              <small class="help-text">机型标识不可修改</small>
+            </div>
+
+            <div class="form-group">
+              <label>设备类型</label>
+              <input v-model="formData.device_type" disabled class="disabled-input" />
+              <small class="help-text">设备类型不可修改</small>
+            </div>
+
+            <div class="form-group">
+              <label>模板类型</label>
+              <input v-model="formData.template_type" disabled class="disabled-input" />
+              <small class="help-text">模板类型不可修改</small>
+            </div>
+
+            <div class="form-group">
+              <label>二级Key</label>
+              <input v-model="formData.redis_key_second" />
+            </div>
+
+            <div class="form-group">
+              <label>一级Key(JSON数组)</label>
+              <textarea v-model="formData.redis_keys_first" rows="3"></textarea>
+            </div>
+
+            <div class="form-group">
+              <label>复用来源</label>
+              <input v-model="formData.reusable_from" disabled class="disabled-input" />
+              <small class="help-text" v-if="formData.reusable_from">此模板复用于 {{ formData.reusable_from }}</small>
+              <small class="help-text" v-else>原创模板(未被复用)</small>
+            </div>
+
+            <div class="form-group" v-if="!formData.reusable_from">
+              <label>模板正文 *</label>
+              <textarea class="template-body-input" v-model="formData.template_body" rows="10" placeholder="Jinja2模板内容..."></textarea>
+              <small class="help-text">原创模板可修改正文,复用模板的正文由源模板控制</small>
+            </div>
+          </div>
+
+          <!-- 新增模式:显示模式切换 -->
+          <div v-if="!isEditing">
+            <!-- 模式切换 -->
+            <div class="mode-tabs">
+              <button class="tab" :class="{ active: addMode === 'reuse' }" @click="addMode = 'reuse'">
+                🔗 复用模式
+              </button>
+              <button class="tab" :class="{ active: addMode === 'create' }" @click="addMode = 'create'">
+                📝 新建模式
+              </button>
+            </div>
+
+            <!-- 复用模式 -->
+            <div v-if="addMode === 'reuse'" class="form-section">
+              <div class="form-group">
+                <label>选择源模板 *(可以选择任意机型的原创模板)</label>
+                <select v-model="formData.reusable_from">
+                  <option value="">请选择源模板</option>
+                  <option v-for="src in allOriginalTemplates" :key="src.id" :value="src.id">
+                    {{ src.machine_model }} / {{ src.template_type }} / {{ src.redis_key_second }}
+                  </option>
+                </select>
+              </div>
+          
+              <div class="form-group">
+                <label>目标机型标识 *</label>
+                <input v-model="formData.machine_model" placeholder="如: x11, hplus, 或输入新机型的标识" />
+                <small class="help-text">输入已有机型标识或新机型名称</small>
+              </div>
+          
+              <div class="form-group">
+                <label>设备类型 *</label>
+                <select v-model="formData.device_type">
+                  <option value="">请选择</option>
+                  <option value="wash">洗衣机</option>
+                  <option value="dry">干衣机</option>
+                </select>
+              </div>
+          
+              <div class="form-group">
+                <label>模板类型 *</label>
+                <input v-model="formData.template_type" placeholder="如: time_popular" />
+              </div>
+          
+              <div class="form-group">
+                <label>二级Key *</label>
+                <input v-model="formData.redis_key_second" placeholder="broadcast-template-jinja:single_card:wash:time_popular" />
+              </div>
+          
+              <div class="form-group">
+                <label>一级Key(可选,JSON数组)</label>
+                <textarea v-model="formData.redis_keys_first" placeholder='=["key1", "key2"]'></textarea>
+              </div>
+            </div>
+
+            <!-- 新建模式 -->
+            <div v-if="addMode === 'create'" class="form-section">
+              <div class="form-group">
+                <label>机型标识 *</label>
+                <input v-model="formData.machine_model" placeholder="如: x11, hplus, x12(输入已有机型或新机型标识)" />
+                <small class="help-text">输入已有机型标识直接使用,输入新标识将自动创建机型配置</small>
+              </div>
+
+              <div class="form-group">
+                <label>显示名称</label>
+                <input v-model="formData.machine_label" placeholder="如: X11, HPlus(可选,用于机型管理显示)" />
+              </div>
+
+              <div class="form-group">
+                <label>设备类型 *</label>
+                <select v-model="formData.device_type">
+                  <option value="">请选择</option>
+                  <option value="wash">洗衣机</option>
+                  <option value="dry">干衣机</option>
+                </select>
+              </div>
+
+              <div class="form-group">
+                <label>模板类型 *</label>
+                <input v-model="formData.template_type" placeholder="如: time_popular" />
+              </div>
+
+              <div class="form-group">
+                <label>二级Key *</label>
+                <input v-model="formData.redis_key_second" placeholder="broadcast-template-jinja:single_card:wash:time_popular" />
+              </div>
+
+              <div class="form-group">
+                <label>一级Key(可选,JSON数组)</label>
+                <textarea v-model="formData.redis_keys_first" placeholder='=["key1", "key2"]'></textarea>
+              </div>
+
+              <div class="form-group">
+                <label>模板正文 *</label>
+                <textarea class="template-body-input" v-model="formData.template_body" rows="10" placeholder="{% set greeting = ['嗨', '哈喽'] %}..."></textarea>
+              </div>
+            </div>
+
+          </div>
+
+          <div class="form-actions">
+            <button class="btn btn-secondary" @click="showAdd = false">取消</button>
+            <button class="btn btn-primary" @click="saveTemplate" :disabled="saving">
+              {{ saving ? '保存中...' : (isEditing ? '保存' : '创建') }}
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 机型管理弹窗 -->
+    <div class="modal-overlay" v-if="showMachineManage" @click.self="showMachineManage = false">
+      <div class="modal modal-machine">
+        <div class="modal-header">
+          <h3>⚙ 机型管理</h3>
+          <button class="btn-close" @click="showMachineManage = false">×</button>
+        </div>
+        <div class="modal-body">
+          <div class="machine-list">
+            <table class="machine-table">
+              <thead>
+                <tr>
+                  <th>机型标识</th>
+                  <th>显示名称</th>
+                  <th>卡片类型</th>
+                  <th>复用来源</th>
+                  <th>操作</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(config, key) in machineConfigs" :key="key">
+                  <td>{{ key }}</td>
+                  <td>{{ config.label }}</td>
+                  <td>{{ config.card_type }}</td>
+                  <td>{{ reuseMap[key]?.all || '-' }}</td>
+                  <td class="action-cell">
+                    <button class="btn-sm btn-edit" @click="editMachine(key)">✏ 编辑</button>
+                    <button class="btn-sm btn-delete" @click="deleteMachine(key)">🗑 删除</button>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+
+          <div class="machine-form-section">
+            <h4>{{ machineForm.id ? '编辑机型' : '新增机型' }}</h4>
+            <div class="form-group">
+              <label>机型标识 *</label>
+              <input v-model="machineForm.id" :disabled="!!machineForm.id" placeholder="如: x12" />
+            </div>
+            <div class="form-group">
+              <label>显示名称 *</label>
+              <input v-model="machineForm.label" placeholder="如: X12" />
+            </div>
+            <div class="form-group">
+              <label>卡片类型 *</label>
+              <select v-model="machineForm.card_type">
+                <option value="single_card">单卡</option>
+                <option value="multi_card">多卡</option>
+              </select>
+            </div>
+            <div class="form-group">
+              <label>复用来源(可选)</label>
+              <select v-model="machineForm.reuse_from">
+                <option value="">无(独立机型)</option>
+                <option v-for="(config, key) in machineConfigs" :key="key" :value="key" :disabled="key === machineForm.id">
+                  {{ config.label }} ({{ key }})
+                </option>
+              </select>
+            </div>
+            <div class="form-group">
+              <label>设备Type ID映射 * (JSON)</label>
+              <textarea v-model="machineForm.type_ids_str" rows="3" placeholder='{"wash": "type_id_1", "dry": "type_id_2"}'></textarea>
+            </div>
+            <div class="form-group">
+              <label>设备Zone ID映射 * (JSON)</label>
+              <textarea v-model="machineForm.zone_ids_str" rows="3" placeholder='{"wash": ["2", "3"], "dry": ["0"]}'></textarea>
+            </div>
+            <div class="form-actions">
+              <button class="btn btn-secondary" @click="resetMachineForm">重置</button>
+              <button class="btn btn-primary" @click="saveMachine" :disabled="savingMachine">
+                {{ savingMachine ? '保存中...' : (machineForm.id ? '更新' : '创建') }}
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 删除确认弹窗 -->
+    <div class="modal-overlay" v-if="showConfirm" @click.self="showConfirm = false">
+      <div class="modal modal-confirm">
+        <div class="modal-header">
+          <h3>⚠ {{ confirmData.title }}</h3>
+          <button class="btn-close" @click="showConfirm = false">×</button>
+        </div>
+        <div class="modal-body">
+          <p class="confirm-message">{{ confirmData.message }}</p>
+          <div class="form-actions">
+            <button class="btn btn-secondary" @click="showConfirm = false">取消</button>
+            <button class="btn btn-danger" @click="confirmData.onConfirm">删除</button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed, watch } from 'vue'
+import { TemplateAPI as api, MachineAPI } from './services/api'
+
+// 状态
+const templates = ref([])
+const machineModels = ref([])
+const deviceTypes = ref([])
+const sidebarCollapsed = ref(false)
+
+const filters = reactive({
+  machine_model: null,
+  device_type: null,
+  template_type: null
+})
+
+// 预览
+const showPreview = ref(false)
+const previewing = ref(false)
+const previewData = reactive({
+  template: null,
+  template_body: '',
+  common_body: '',    // 关联的common模板正文
+  result: '',
+  error: '',
+  condition_vars: [],  // 条件分支变量
+  data_vars: [],       // 数据变量
+  context_used: {}     // 实际使用的变量值
+})
+
+// 变量变更时自动重新渲染
+let renderTimeout = null
+const onVariableChange = () => {
+  clearTimeout(renderTimeout)
+  renderTimeout = setTimeout(() => {
+    regeneratePreview()
+  }, 500)  // 500ms 防抖
+}
+
+// 删除确认弹窗
+const showConfirm = ref(false)
+const confirmData = reactive({
+  title: '确认删除',
+  message: '',
+  onConfirm: null
+})
+
+// 新增/编辑
+const showAdd = ref(false)
+const isEditing = ref(false)
+const addMode = ref('reuse') // 'reuse' | 'create'
+const saving = ref(false)
+
+// 机型管理
+const showMachineManage = ref(false)
+const machineConfigs = ref({})
+const reuseMap = ref({})
+const savingMachine = ref(false)
+
+const machineForm = reactive({
+  id: '',
+  label: '',
+  card_type: 'single_card',
+  reuse_from: '',
+  type_ids_str: '',
+  zone_ids_str: ''
+})
+
+const formData = reactive({
+  id: null,
+  machine_model: '',
+  machine_label: '',  // 显示名称
+  device_type: '',
+  template_type: '',
+  redis_key_second: '',
+  redis_keys_first: '',
+  template_body: '',
+  reusable_from: ''
+})
+
+// 新建机型表单
+const newMachineForm = reactive({
+  model_key: '',
+  label: '',
+  card_type: 'single_card',
+  type_ids_str: '',
+  zone_ids_str: ''
+})
+
+// 计算属性
+const originalTemplates = computed(() => {
+  return templates.value.filter(t => !t.reusable_from && t.template_body)
+})
+
+// 所有原创模板(不按机型筛选)
+const allOriginalTemplates = computed(() => {
+  return templates.value.filter(t => !t.reusable_from && t.template_body)
+})
+
+// 方法
+const toggleSidebar = () => {
+  sidebarCollapsed.value = !sidebarCollapsed.value
+}
+
+const getModelIcon = (model) => {
+  const icons = {
+    'x11': '🔵',
+    'hplus': '🟢',
+    'f2_multi': '🟡',
+    'ultra': '🟣',
+    'seeker': '🟠',
+    'seekerR2': '⚪',
+    'x11_ultra': '🔷'
+  }
+  return icons[model] || '📦'
+}
+
+const loadTemplates = async () => {
+  const params = {}
+  if (filters.machine_model) params.machine_model = filters.machine_model
+  if (filters.device_type) params.device_type = filters.device_type
+  
+  const data = await api.listTemplates(params)
+  templates.value = Array.isArray(data) ? data : (data.data || [])
+}
+
+const loadMachineModels = async () => {
+  const data = await api.listMachineModels()
+  machineModels.value = data.machine_models || []
+}
+
+const loadDeviceTypes = async () => {
+  if (!filters.machine_model) {
+    deviceTypes.value = []
+    return
+  }
+  const data = await api.listDeviceTypes(filters.machine_model)
+  deviceTypes.value = data.device_types || []
+}
+
+// 监听机型变化,加载对应的设备类型
+watch(() => filters.machine_model, () => {
+  loadDeviceTypes()
+})
+
+const previewTemplate = async (tpl) => {
+  previewData.template = tpl
+  previewData.template_body = ''
+  previewData.common_body = ''
+  previewData.result = ''
+  previewData.error = ''
+  previewData.condition_vars = []
+  previewData.data_vars = []
+  previewData.context_used = {}
+  showPreview.value = true
+  
+  // 获取模板正文
+  let body = tpl.template_body
+  if (!body && tpl.reusable_from) {
+    try {
+      const srcRes = await api.getTemplate(tpl.reusable_from)
+      body = srcRes.template_body || ''
+    } catch (err) {
+      previewData.error = '获取源模板失败: ' + err.message
+      return
+    }
+  }
+
+  if (!body) {
+    previewData.error = '模板正文为空,无法预览'
+    return
+  }
+
+  previewData.template_body = body
+  
+  // 如果不是common模板,查找对应的common模板获取宏定义
+  if (tpl.template_type && tpl.template_type !== 'common') {
+    const commonId = `${tpl.machine_model}_${tpl.device_type}_common`
+    const commonTpl = templates.value.find(t => t.id === commonId)
+    if (commonTpl && commonTpl.template_body) {
+      previewData.common_body = commonTpl.template_body
+    } else if (commonTpl && commonTpl.reusable_from) {
+      // common模板是复用的,获取源模板
+      try {
+        const srcRes = await api.getTemplate(commonTpl.reusable_from)
+        previewData.common_body = srcRes.template_body || ''
+      } catch (err) {
+        console.warn('获取common模板失败:', err)
+      }
+    }
+  }
+  
+  // 直接渲染,后端会返回变量列表
+  await doPreview()
+}
+
+const regeneratePreview = async () => {
+  await doPreview()
+}
+
+const doPreview = async () => {
+  if (!previewData.template_body) {
+    previewData.error = '模板正文为空'
+    return
+  }
+
+  // 收集所有变量值(条件变量 + 数据变量)
+  const parameters = {}
+  for (const v of previewData.condition_vars) {
+    if (v.value !== undefined && v.value !== '') {
+      parameters[v.name] = v.value
+    }
+  }
+  for (const v of previewData.data_vars) {
+    if (v.value !== undefined && v.value !== '') {
+      parameters[v.name] = v.value
+    }
+  }
+
+  previewing.value = true
+  
+  try {
+    const result = await api.previewTemplate({ 
+      template_body: previewData.template_body,
+      parameters,
+      common_body: previewData.common_body || undefined
+    })
+    previewData.result = result.result || ''
+    previewData.error = result.error || ''
+    // 更新变量列表,保留用户已设置的值
+    if (result.condition_vars) {
+      const existing = {}
+      for (const v of previewData.condition_vars) {
+        existing[v.name] = v.value
+      }
+      previewData.condition_vars = result.condition_vars.map(v => ({
+        ...v,
+        value: v.name in existing ? existing[v.name] : v.value
+      }))
+    }
+    if (result.data_vars) {
+      const existing = {}
+      for (const v of previewData.data_vars) {
+        existing[v.name] = v.value
+      }
+      previewData.data_vars = result.data_vars.map(v => ({
+        ...v,
+        value: v.name in existing ? existing[v.name] : v.value
+      }))
+    }
+    if (result.context_used) {
+      previewData.context_used = result.context_used
+    }
+  } catch (err) {
+    previewData.result = ''
+    previewData.error = '渲染失败: ' + err.message
+  } finally {
+    previewing.value = false
+  }
+}
+
+const showAddDialog = () => {
+  isEditing.value = false
+  addMode.value = 'reuse'
+  resetForm()
+  showAdd.value = true
+}
+
+const editTemplate = (tpl) => {
+  isEditing.value = true
+  addMode.value = tpl.reusable_from ? 'reuse' : 'create'
+  
+  formData.id = tpl.id
+  formData.machine_model = tpl.machine_model
+  formData.device_type = tpl.device_type
+  formData.template_type = tpl.template_type
+  formData.redis_key_second = tpl.redis_key_second || ''
+  formData.redis_keys_first = JSON.stringify(tpl.redis_keys_first || [])
+  formData.template_body = tpl.template_body || ''
+  formData.reusable_from = tpl.reusable_from || ''
+  
+  showAdd.value = true
+}
+
+const deleteTemplate = (tpl) => {
+  confirmData.title = '确认删除模板'
+  confirmData.message = `确定要删除模板 "${tpl.name || tpl.id}" 吗?此操作不可恢复。`
+  confirmData.onConfirm = async () => {
+    showConfirm.value = false
+    try {
+      await api.deleteTemplate(tpl.id)
+      await loadTemplates()
+    } catch (err) {
+      alert('删除失败: ' + (err.response?.data?.detail || err.message))
+    }
+  }
+  showConfirm.value = true
+}
+
+const resetForm = () => {
+  formData.id = null
+  formData.machine_model = ''
+  formData.machine_label = ''
+  formData.device_type = ''
+  formData.template_type = ''
+  formData.redis_key_second = ''
+  formData.redis_keys_first = ''
+  formData.template_body = ''
+  formData.reusable_from = ''
+}
+
+const onMachineModelChange = () => {
+  // 当选择新建机型时,清空相关字段
+  if (formData.machine_model === '__new__') {
+    resetForm()
+    formData.machine_model = '__new__'
+  }
+}
+
+const saveTemplate = async () => {
+  // 编辑模式:只允许修改部分字段
+  if (isEditing.value) {
+    // 验证二级Key
+    if (!formData.redis_key_second) {
+      alert('请填写二级Key')
+      return
+    }
+
+    saving.value = true
+    
+    try {
+      let redisKeysFirst = []
+      if (formData.redis_keys_first) {
+        try {
+          redisKeysFirst = JSON.parse(formData.redis_keys_first)
+        } catch {
+          alert('一级Key格式错误,请输入JSON数组')
+          saving.value = false
+          return
+        }
+      }
+
+      // 编辑模式只发送允许修改的字段
+      const payload = {
+        redis_key_second: formData.redis_key_second,
+        redis_keys_first: redisKeysFirst,
+      }
+
+      // 如果是原创模板,允许修改正文
+      if (!formData.reusable_from) {
+        if (!formData.template_body) {
+          alert('原创模板必须填写模板正文')
+          saving.value = false
+          return
+        }
+        payload.template_body = formData.template_body
+      }
+
+      await api.updateTemplate(formData.id, payload)
+      showAdd.value = false
+      await loadTemplates()
+    } catch (err) {
+      alert('保存失败: ' + (err.response?.data?.detail || err.message))
+    } finally {
+      saving.value = false
+    }
+    return
+  }
+
+  // 新增模式逻辑
+  if (!formData.machine_model || !formData.device_type || !formData.template_type || !formData.redis_key_second) {
+    alert('请填写必填字段')
+    return
+  }
+
+  if (addMode.value === 'create' && !formData.template_body) {
+    alert('新建模式必须填写模板正文')
+    return
+  }
+
+  if (addMode.value === 'reuse' && !formData.reusable_from) {
+    alert('复用模式必须选择源模板')
+    return
+  }
+
+  // 如果是新建机型,先创建机型配置
+  let actualMachineModel = formData.machine_model
+  if (formData.machine_model === '__new__') {
+    // 验证新机表单
+    if (!newMachineForm.model_key || !newMachineForm.label) {
+      alert('请填写机型标识和显示名称')
+      return
+    }
+
+    let typeIds, zoneIds
+    try {
+      typeIds = JSON.parse(newMachineForm.type_ids_str)
+    } catch {
+      alert('Type ID映射格式错误,请输入有效的JSON')
+      return
+    }
+
+    try {
+      zoneIds = JSON.parse(newMachineForm.zone_ids_str)
+    } catch {
+      alert('Zone ID映射格式错误,请输入有效的JSON')
+      return
+    }
+
+    try {
+      await MachineAPI.createMachineConfig({
+        model_key: newMachineForm.model_key,
+        label: newMachineForm.label,
+        type_ids: typeIds,
+        zone_ids: zoneIds,
+        card_type: newMachineForm.card_type
+      })
+      actualMachineModel = newMachineForm.model_key
+      
+      // 刷新机型列表
+      await loadMachineModels()
+    } catch (err) {
+      alert('创建机型失败: ' + err.message)
+      return
+    }
+  }
+
+  saving.value = true
+  
+  try {
+    let redisKeysFirst = []
+    if (formData.redis_keys_first) {
+      try {
+        redisKeysFirst = JSON.parse(formData.redis_keys_first)
+      } catch {
+        alert('一级Key格式错误,请输入JSON数组')
+        saving.value = false
+        return
+      }
+    }
+
+    const payload = {
+      machine_model: actualMachineModel,
+      device_type: formData.device_type,
+      template_type: formData.template_type,
+      redis_key_second: formData.redis_key_second,
+      redis_keys_first: redisKeysFirst,
+      template_body: formData.template_body,
+      reusable_from: formData.reusable_from || null
+    }
+
+    if (isEditing.value && formData.id) {
+      await api.updateTemplate(formData.id, payload)
+    } else {
+      await api.createTemplate(payload)
+    }
+
+    showAdd.value = false
+    await loadTemplates()
+  } catch (err) {
+    alert('保存失败: ' + (err.response?.data?.detail || err.message))
+  } finally {
+    saving.value = false
+  }
+}
+
+// 初始化
+onMounted(async () => {
+  await Promise.all([
+    loadMachineModels(),
+    loadTemplates()
+  ])
+})
+
+// 机型管理方法
+const loadMachineConfigs = async () => {
+  try {
+    const data = await MachineAPI.listMachineConfigs()
+    machineConfigs.value = data.configs || {}
+    reuseMap.value = data.reuse_map || {}
+  } catch (err) {
+    console.error('加载机型配置失败:', err)
+  }
+}
+
+const showMachineDialog = () => {
+  resetMachineForm()
+  showMachineManage.value = true
+  loadMachineConfigs()
+}
+
+const editMachine = (key) => {
+  const config = machineConfigs.value[key]
+  if (!config) return
+
+  machineForm.id = key
+  machineForm.label = config.label
+  machineForm.card_type = config.card_type || 'single_card'
+  machineForm.reuse_from = reuseMap.value[key]?.all || ''
+  machineForm.type_ids_str = JSON.stringify(config.type_ids, null, 2)
+  machineForm.zone_ids_str = JSON.stringify(config.zone_ids, null, 2)
+}
+
+const deleteMachine = async (key) => {
+  const config = machineConfigs.value[key]
+  if (!confirm(`确认删除机型 "${config.label}" (${key}) 吗?`)) return
+
+  try {
+    await MachineAPI.deleteMachineConfig(key)
+    await loadMachineConfigs()
+    await loadMachineModels()
+  } catch (err) {
+    alert('删除失败: ' + err.message)
+  }
+}
+
+const resetMachineForm = () => {
+  machineForm.id = ''
+  machineForm.label = ''
+  machineForm.card_type = 'single_card'
+  machineForm.reuse_from = ''
+  machineForm.type_ids_str = ''
+  machineForm.zone_ids_str = ''
+}
+
+const saveMachine = async () => {
+  // 验证
+  if (!machineForm.id || !machineForm.label) {
+    alert('请填写机型标识和显示名称')
+    return
+  }
+
+  let typeIds, zoneIds
+  try {
+    typeIds = JSON.parse(machineForm.type_ids_str)
+  } catch {
+    alert('Type ID映射格式错误,请输入有效的JSON')
+    return
+  }
+
+  try {
+    zoneIds = JSON.parse(machineForm.zone_ids_str)
+  } catch {
+    alert('Zone ID映射格式错误,请输入有效的JSON')
+    return
+  }
+
+  savingMachine.value = true
+
+  try {
+    const payload = {
+      model_key: machineForm.id,
+      label: machineForm.label,
+      type_ids: typeIds,
+      zone_ids: zoneIds,
+      card_type: machineForm.card_type,
+      reuse_from: machineForm.reuse_from || null
+    }
+
+    if (machineForm.id && machineConfigs.value[machineForm.id]) {
+      // 更新
+      await MachineAPI.updateMachineConfig(machineForm.id, {
+        label: payload.label,
+        type_ids: payload.type_ids,
+        zone_ids: payload.zone_ids,
+        card_type: payload.card_type,
+        reuse_from: payload.reuse_from
+      })
+    } else {
+      // 创建
+      await MachineAPI.createMachineConfig(payload)
+    }
+
+    resetMachineForm()
+    await loadMachineConfigs()
+    await loadMachineModels()
+  } catch (err) {
+    alert('保存失败: ' + err.message)
+  } finally {
+    savingMachine.value = false
+  }
+}
+</script>
+
+<style scoped>
+/* 布局 */
+.admin-layout {
+  display: flex;
+  height: 100vh;
+  background: #f5f7fa;
+}
+
+/* 左侧蓝色导航 */
+.sidebar {
+  width: 240px;
+  background: linear-gradient(180deg, #1e3a8a 0%, #2563eb 100%);
+  color: white;
+  transition: width 0.3s;
+  overflow: hidden;
+  flex-shrink: 0;
+}
+
+.sidebar.collapsed {
+  width: 60px;
+}
+
+.sidebar-header {
+  padding: 20px;
+  cursor: pointer;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.sidebar-header h2 {
+  margin: 0;
+  font-size: 20px;
+  white-space: nowrap;
+}
+
+.sidebar-nav {
+  padding: 10px;
+}
+
+.nav-section {
+  margin-bottom: 20px;
+}
+
+.nav-title {
+  font-size: 12px;
+  opacity: 0.7;
+  padding: 10px;
+  text-transform: uppercase;
+  letter-spacing: 1px;
+}
+
+.nav-item {
+  padding: 12px;
+  margin: 4px 0;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: all 0.2s;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.nav-item:hover {
+  background: rgba(255, 255, 255, 0.1);
+}
+
+.nav-item.active {
+  background: rgba(255, 255, 255, 0.2);
+  font-weight: 600;
+}
+
+.icon {
+  font-size: 18px;
+}
+
+/* 主内容区 */
+.main-content {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.top-bar {
+  background: white;
+  padding: 20px 30px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+}
+
+.top-bar h1 {
+  margin: 0;
+  font-size: 24px;
+  color: #1e293b;
+}
+
+.content-area {
+  flex: 1;
+  padding: 30px;
+  overflow-y: auto;
+}
+
+/* 表格 */
+.template-table {
+  width: 100%;
+  background: white;
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+.template-table thead {
+  background: #f8fafc;
+}
+
+.template-table th {
+  padding: 15px;
+  text-align: left;
+  font-weight: 600;
+  color: #64748b;
+  font-size: 14px;
+  border-bottom: 2px solid #e2e8f0;
+}
+
+.template-table td {
+  padding: 15px;
+  border-bottom: 1px solid #f1f5f9;
+  font-size: 14px;
+}
+
+.key-cell {
+  max-width: 300px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.action-cell {
+  display: flex;
+  gap: 8px;
+}
+
+/* 徽章 */
+.badge {
+  padding: 4px 12px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: 600;
+}
+
+.badge-original {
+  background: #dbeafe;
+  color: #1e40af;
+}
+
+.badge-reuse {
+  background: #fef3c7;
+  color: #92400e;
+}
+
+/* 按钮 */
+.btn {
+  padding: 10px 20px;
+  border: none;
+  border-radius: 8px;
+  cursor: pointer;
+  font-size: 14px;
+  font-weight: 600;
+  transition: all 0.2s;
+}
+
+.btn-primary {
+  background: #2563eb;
+  color: white;
+}
+
+.btn-primary:hover {
+  background: #1d4ed8;
+}
+
+.btn-secondary {
+  background: #e2e8f0;
+  color: #475569;
+}
+
+.btn-sm {
+  padding: 6px 12px;
+  font-size: 12px;
+  border: none;
+  border-radius: 6px;
+  cursor: pointer;
+}
+
+.btn-preview {
+  background: #10b981;
+  color: white;
+}
+
+.btn-edit {
+  background: #f59e0b;
+  color: white;
+}
+
+.btn-delete {
+  background: #ef4444;
+  color: white;
+}
+
+/* 空状态 */
+.empty-state {
+  text-align: center;
+  padding: 60px;
+  color: #94a3b8;
+}
+
+/* 弹窗 */
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1000;
+}
+
+.modal {
+  background: white;
+  border-radius: 16px;
+  max-width: 900px;
+  width: 90%;
+  max-height: 90vh;
+  display: flex;
+  flex-direction: column;
+  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+}
+
+.modal-header {
+  padding: 20px 30px;
+  border-bottom: 1px solid #e2e8f0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  position: sticky;
+  top: 0;
+  background: white;
+  z-index: 10;
+  border-radius: 16px 16px 0 0;
+  flex-shrink: 0;
+}
+
+.modal-body {
+  padding: 30px;
+  overflow-y: auto;
+  flex: 1;
+}
+
+.modal-header h3 {
+  margin: 0;
+  font-size: 20px;
+  color: #1e293b;
+}
+
+.btn-close {
+  background: none;
+  border: none;
+  font-size: 28px;
+  cursor: pointer;
+  color: #94a3b8;
+  line-height: 1;
+}
+
+.btn-close:hover {
+  color: #ef4444;
+}
+
+.modal-body {
+  padding: 30px;
+}
+
+/* 预览 */
+.preview-info {
+  background: #f8fafc;
+  padding: 15px;
+  border-radius: 8px;
+  margin-bottom: 20px;
+}
+
+.info-row {
+  display: flex;
+  gap: 10px;
+  margin: 8px 0;
+  font-size: 14px;
+}
+
+.info-row label {
+  font-weight: 600;
+  color: #64748b;
+  min-width: 80px;
+}
+
+.info-row span {
+  color: #1e293b;
+}
+
+/* 变量编辑区 */
+.variables-section {
+  background: #f8fafc;
+  border: 2px solid #e2e8f0;
+  border-radius: 12px;
+  padding: 20px;
+  margin-bottom: 20px;
+}
+
+.variables-section h4 {
+  margin: 0 0 15px 0;
+  font-size: 16px;
+  color: #1e293b;
+}
+
+.variables-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+  gap: 15px;
+}
+
+.variable-item {
+  display: flex;
+  flex-direction: column;
+  gap: 5px;
+}
+
+.variable-item label {
+  font-weight: 600;
+  font-size: 13px;
+  color: #475569;
+}
+
+.variable-item .required {
+  color: #ef4444;
+}
+
+.variable-item input {
+  padding: 8px 12px;
+  border: 2px solid #e2e8f0;
+  border-radius: 6px;
+  font-size: 14px;
+  transition: border-color 0.2s;
+}
+
+.variable-item input:focus {
+  outline: none;
+  border-color: #2563eb;
+}
+
+.var-desc {
+  font-size: 11px;
+  color: #94a3b8;
+}
+
+/* 条件变量特殊样式 */
+.condition-section {
+  border-color: #f59e0b;
+  background: #fffbeb;
+}
+
+.condition-section h4 {
+  color: #b45309;
+}
+
+.var-badge {
+  display: inline-block;
+  background: #f59e0b;
+  color: white;
+  font-size: 10px;
+  padding: 2px 6px;
+  border-radius: 4px;
+  margin-left: 4px;
+  font-weight: 600;
+}
+
+.data-section {
+  border-color: #10b981;
+  background: #ecfdf5;
+}
+
+.data-section h4 {
+  color: #047857;
+}
+
+.variable-item select {
+  padding: 8px 12px;
+  border: 2px solid #e2e8f0;
+  border-radius: 6px;
+  font-size: 14px;
+  background: white;
+}
+
+.variable-item select:focus {
+  outline: none;
+  border-color: #2563eb;
+}
+
+.context-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+}
+
+.context-tags .tag {
+  background: #e0e7ff;
+  color: #4338ca;
+  padding: 2px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+}
+
+/* 播报语结果区域 - 最重要 */
+.preview-result-section {
+  background: #ffffff;
+  border: 2px solid #2563eb;
+  border-radius: 12px;
+  padding: 20px;
+  margin-bottom: 20px;
+}
+
+.result-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding-bottom: 15px;
+  border-bottom: 2px solid #e2e8f0;
+}
+
+.result-header h4 {
+  margin: 0;
+  font-size: 18px;
+  color: #1e293b;
+}
+
+.btn-regen {
+  background: #10b981;
+  color: white;
+  padding: 8px 16px;
+  font-weight: 600;
+}
+
+.btn-regen:hover:not(:disabled) {
+  background: #059669;
+}
+
+.btn-regen:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.result-content {
+  background: #f0f9ff;
+  border-left: 4px solid #2563eb;
+  padding: 20px;
+  border-radius: 8px;
+  min-height: 100px;
+}
+
+.result-text {
+  font-size: 16px;
+  line-height: 1.8;
+  color: #1e293b;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+
+.result-empty {
+  text-align: center;
+  padding: 40px;
+  color: #94a3b8;
+  font-size: 14px;
+}
+
+.preview-error {
+  background: #fef2f2;
+  border: 2px solid #ef4444;
+  border-radius: 8px;
+  padding: 15px;
+  margin-bottom: 20px;
+}
+
+.preview-error h4 {
+  margin: 0 0 10px 0;
+  color: #dc2626;
+  font-size: 14px;
+}
+
+.preview-error pre {
+  margin: 0;
+  color: #991b1b;
+  font-size: 13px;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+}
+
+.preview-context {
+  background: #f8fafc;
+  border-radius: 8px;
+  padding: 15px;
+  margin-top: 20px;
+}
+
+.preview-context summary {
+  cursor: pointer;
+  font-weight: 600;
+  color: #475569;
+  font-size: 14px;
+  padding: 5px 0;
+}
+
+.preview-context summary:hover {
+  color: #2563eb;
+}
+
+.context-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+  gap: 10px;
+  margin-top: 15px;
+  padding-top: 15px;
+  border-top: 1px solid #e2e8f0;
+}
+
+.context-item {
+  background: white;
+  padding: 10px;
+  border-radius: 6px;
+  font-size: 12px;
+  border: 1px solid #e2e8f0;
+}
+
+.var-name {
+  font-weight: 600;
+  color: #2563eb;
+  display: block;
+  margin-bottom: 4px;
+}
+
+.var-value {
+  color: #475569;
+  word-break: break-all;
+}
+
+/* 表单 */
+.mode-tabs {
+  display: flex;
+  gap: 10px;
+  margin-bottom: 20px;
+}
+
+.tab {
+  flex: 1;
+  padding: 12px;
+  border: 2px solid #e2e8f0;
+  background: white;
+  border-radius: 8px;
+  cursor: pointer;
+  font-weight: 600;
+  transition: all 0.2s;
+}
+
+.tab.active {
+  border-color: #2563eb;
+  background: #eff6ff;
+  color: #2563eb;
+}
+
+.form-section {
+  margin-top: 20px;
+}
+
+.edit-mode {
+  background: #f8fafc;
+  padding: 20px;
+  border-radius: 8px;
+  border: 2px solid #e2e8f0;
+}
+
+.disabled-input {
+  background: #f1f5f9;
+  color: #64748b;
+  cursor: not-allowed;
+}
+
+.form-group {
+  margin-bottom: 20px;
+}
+
+.form-group label {
+  display: block;
+  margin-bottom: 8px;
+  font-weight: 600;
+  color: #475569;
+}
+
+.form-group input,
+.form-group select,
+.form-group textarea {
+  width: 100%;
+  padding: 10px;
+  border: 2px solid #e2e8f0;
+  border-radius: 8px;
+  font-size: 14px;
+  font-family: inherit;
+}
+
+.form-group textarea {
+  font-family: 'Courier New', monospace;
+  resize: vertical;
+}
+
+.template-body-input {
+  min-height: 200px;
+}
+
+.form-actions {
+  display: flex;
+  gap: 10px;
+  justify-content: flex-end;
+  margin-top: 30px;
+}
+
+.help-text {
+  display: block;
+  margin-top: 4px;
+  font-size: 12px;
+  color: #94a3b8;
+}
+
+/* 机型管理弹窗样式 */
+.modal-machine {
+  max-width: 1000px;
+  width: 90%;
+}
+
+.machine-list {
+  margin-bottom: 30px;
+}
+
+.machine-table {
+  width: 100%;
+  background: white;
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+.machine-table thead {
+  background: #f8fafc;
+}
+
+.machine-table th {
+  padding: 12px;
+  text-align: left;
+  font-weight: 600;
+  color: #64748b;
+  font-size: 13px;
+  border-bottom: 2px solid #e2e8f0;
+}
+
+.machine-table td {
+  padding: 12px;
+  border-bottom: 1px solid #f1f5f9;
+  font-size: 13px;
+}
+
+.machine-form-section {
+  background: #f8fafc;
+  padding: 20px;
+  border-radius: 8px;
+  margin-top: 20px;
+}
+
+.machine-form-section h4 {
+  margin: 0 0 20px 0;
+  color: #1e293b;
+  font-size: 16px;
+}
+
+/* 确认弹窗 */
+.modal-confirm {
+  max-width: 450px;
+}
+
+.confirm-message {
+  font-size: 16px;
+  color: #475569;
+  margin-bottom: 25px;
+  line-height: 1.6;
+}
+
+.btn-danger {
+  background: #ef4444;
+  color: white;
+}
+
+.btn-danger:hover {
+  background: #dc2626;
+}
+</style>

+ 1 - 0
frontend/src/assets/vue.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

+ 43 - 0
frontend/src/components/HelloWorld.vue

@@ -0,0 +1,43 @@
+<script setup>
+import { ref } from 'vue'
+
+defineProps({
+  msg: String,
+})
+
+const count = ref(0)
+</script>
+
+<template>
+  <h1>{{ msg }}</h1>
+
+  <div class="card">
+    <button type="button" @click="count++">count is {{ count }}</button>
+    <p>
+      Edit
+      <code>components/HelloWorld.vue</code> to test HMR
+    </p>
+  </div>
+
+  <p>
+    Check out
+    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
+      >create-vue</a
+    >, the official Vue + Vite starter
+  </p>
+  <p>
+    Learn more about IDE Support for Vue in the
+    <a
+      href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
+      target="_blank"
+      >Vue Docs Scaling up Guide</a
+    >.
+  </p>
+  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
+</template>
+
+<style scoped>
+.read-the-docs {
+  color: #888;
+}
+</style>

+ 5 - 0
frontend/src/main.js

@@ -0,0 +1,5 @@
+import { createApp } from 'vue'
+import './style.css'
+import App from './App.vue'
+
+createApp(App).mount('#app')

+ 103 - 0
frontend/src/services/api.js

@@ -0,0 +1,103 @@
+const API_BASE = import.meta.env.VITE_API_BASE ?? 'http://localhost:8000/api'
+
+async function request(path, options = {}) {
+  // 添加时间戳防止缓存
+  const sep = path.includes('?') ? '&' : '?'
+  const url = `${API_BASE}${path}${sep}_t=${Date.now()}`
+  const response = await fetch(url, {
+    headers: {
+      'Content-Type': 'application/json',
+      ...(options.headers ?? {})
+    },
+    ...options
+  })
+
+  if (!response.ok) {
+    const message = await response.text()
+    throw new Error(message || '请求失败')
+  }
+  if (response.status === 204) {
+    return null
+  }
+  return response.json()
+}
+
+export const TemplateAPI = {
+  listTemplates(params = {}) {
+    const queryString = new URLSearchParams()
+    if (params.machine_model) queryString.append('machine_model', params.machine_model)
+    if (params.device_type) queryString.append('device_type', params.device_type)
+    if (params.template_type) queryString.append('template_type', params.template_type)
+    const url = queryString.toString() ? `/templates?${queryString.toString()}` : '/templates'
+    return request(url)
+  },
+  listCategories(machine_model = null) {
+    const url = machine_model ? `/templates/categories?machine_model=${machine_model}` : '/templates/categories'
+    return request(url)
+  },
+  listMachineModels() {
+    return request('/templates/machine-models')
+  },
+  listDeviceTypes(machine_model = null) {
+    const url = machine_model ? `/templates/device-types?machine_model=${machine_model}` : '/templates/device-types'
+    return request(url)
+  },
+  listTemplateTypes(machine_model = null, device_type = null) {
+    const params = new URLSearchParams()
+    if (machine_model) params.append('machine_model', machine_model)
+    if (device_type) params.append('device_type', device_type)
+    const url = params.toString() ? `/templates/template-types?${params.toString()}` : '/templates/template-types'
+    return request(url)
+  },
+  createTemplate(payload) {
+    return request('/templates', {
+      method: 'POST',
+      body: JSON.stringify(payload)
+    })
+  },
+  updateTemplate(id, payload) {
+    return request(`/templates/${id}`, {
+      method: 'PUT',
+      body: JSON.stringify(payload)
+    })
+  },
+  deleteTemplate(id) {
+    return request(`/templates/${id}`, { method: 'DELETE' })
+  },
+  getTemplate(id) {
+    return request(`/templates/${id}`)
+  },
+  previewTemplate(payload) {
+    return request('/templates/preview', {
+      method: 'POST',
+      body: JSON.stringify(payload)
+    })
+  },
+  renderTemplate(payload) {
+    return request('/templates/render', {
+      method: 'POST',
+      body: JSON.stringify(payload)
+    })
+  }
+}
+
+export const MachineAPI = {
+  listMachineConfigs() {
+    return request('/machine-configs')
+  },
+  createMachineConfig(payload) {
+    return request('/machine-configs', {
+      method: 'POST',
+      body: JSON.stringify(payload)
+    })
+  },
+  updateMachineConfig(modelKey, payload) {
+    return request(`/machine-configs/${modelKey}`, {
+      method: 'PUT',
+      body: JSON.stringify(payload)
+    })
+  },
+  deleteMachineConfig(modelKey) {
+    return request(`/machine-configs/${modelKey}`, { method: 'DELETE' })
+  }
+}

+ 97 - 0
frontend/src/style.css

@@ -0,0 +1,97 @@
+:root {
+  font-family: "Inter", "PingFang SC", system-ui, -apple-system, BlinkMacSystemFont,
+    "Segoe UI", sans-serif;
+  line-height: 1.6;
+  font-weight: 400;
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  color: #0b1c33;
+  background-color: #f5f7fb;
+}
+
+* {
+  box-sizing: border-box;
+}
+
+body {
+  margin: 0;
+  background: #f5f7fb;
+}
+
+#app {
+  min-height: 100vh;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin: 0;
+  font-weight: 600;
+  color: #0f172a;
+}
+
+button {
+  border: none;
+  border-radius: 6px;
+  padding: 0.55rem 1.1rem;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  background-color: #2563eb;
+  color: #fff;
+}
+
+button[disabled] {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+button.ghost {
+  background: transparent;
+  color: #2563eb;
+  border: 1px solid rgba(37, 99, 235, 0.5);
+}
+
+button.ghost.small {
+  padding: 0.35rem 0.6rem;
+  font-size: 0.8rem;
+}
+
+input,
+select,
+textarea {
+  width: 100%;
+  padding: 0.55rem 0.75rem;
+  border-radius: 6px;
+  border: 1px solid #d4d8e6;
+  background: #fff;
+  font-size: 0.95rem;
+}
+
+textarea {
+  resize: vertical;
+  min-height: 180px;
+  font-family: "JetBrains Mono", Consolas, monospace;
+}
+
+label {
+  font-size: 0.85rem;
+  color: #475467;
+  margin-bottom: 0.3rem;
+  display: inline-flex;
+  align-items: center;
+  gap: 0.25rem;
+}
+
+.tag {
+  background: #e0e7ff;
+  color: #1e3a8a;
+  padding: 0.15rem 0.4rem;
+  border-radius: 4px;
+  font-size: 0.75rem;
+  font-weight: 600;
+}

+ 11 - 0
frontend/vite.config.js

@@ -0,0 +1,11 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vite.dev/config/
+export default defineConfig({
+  plugins: [vue()],
+  server:{
+    host:'0.0.0.0',
+    port:'8020'
+  }
+})

+ 7 - 0
import_output.txt

@@ -0,0 +1,7 @@
+================================================================================
+Traceback (most recent call last):
+  File "D:\00_26code\x11_template_vue\scripts\import_templates.py", line 533, in <module>
+    main()
+  File "D:\00_26code\x11_template_vue\scripts\import_templates.py", line 482, in main
+    print("\U0001f680 语音播报模板导入工具")
+UnicodeEncodeError: 'gbk' codec can't encode character '\U0001f680' in position 0: illegal multibyte sequence

+ 3 - 0
main.py

@@ -0,0 +1,3 @@
+from backend.app.main import app
+
+# 这个入口仅用于 `uvicorn main:app` 的快捷引用。

+ 12 - 0
node_modules/.bin/esbuild

@@ -0,0 +1,12 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  exec "$basedir/node"  "$basedir/../esbuild/bin/esbuild" "$@"
+else 
+  exec node  "$basedir/../esbuild/bin/esbuild" "$@"
+fi

+ 17 - 0
node_modules/.bin/esbuild.cmd

@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+  SET "_prog=%dp0%\node.exe"
+) ELSE (
+  SET "_prog=node"
+  SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\esbuild\bin\esbuild" %*

+ 28 - 0
node_modules/.bin/esbuild.ps1

@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+  # Fix case when both the Windows and Linux builds of Node
+  # are installed in the same directory
+  $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "$basedir/node$exe"  "$basedir/../esbuild/bin/esbuild" $args
+  } else {
+    & "$basedir/node$exe"  "$basedir/../esbuild/bin/esbuild" $args
+  }
+  $ret=$LASTEXITCODE
+} else {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "node$exe"  "$basedir/../esbuild/bin/esbuild" $args
+  } else {
+    & "node$exe"  "$basedir/../esbuild/bin/esbuild" $args
+  }
+  $ret=$LASTEXITCODE
+}
+exit $ret

+ 12 - 0
node_modules/.bin/nanoid

@@ -0,0 +1,12 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  exec "$basedir/node"  "$basedir/../nanoid/bin/nanoid.cjs" "$@"
+else 
+  exec node  "$basedir/../nanoid/bin/nanoid.cjs" "$@"
+fi

+ 17 - 0
node_modules/.bin/nanoid.cmd

@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+  SET "_prog=%dp0%\node.exe"
+) ELSE (
+  SET "_prog=node"
+  SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\nanoid\bin\nanoid.cjs" %*

+ 28 - 0
node_modules/.bin/nanoid.ps1

@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+  # Fix case when both the Windows and Linux builds of Node
+  # are installed in the same directory
+  $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "$basedir/node$exe"  "$basedir/../nanoid/bin/nanoid.cjs" $args
+  } else {
+    & "$basedir/node$exe"  "$basedir/../nanoid/bin/nanoid.cjs" $args
+  }
+  $ret=$LASTEXITCODE
+} else {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "node$exe"  "$basedir/../nanoid/bin/nanoid.cjs" $args
+  } else {
+    & "node$exe"  "$basedir/../nanoid/bin/nanoid.cjs" $args
+  }
+  $ret=$LASTEXITCODE
+}
+exit $ret

+ 12 - 0
node_modules/.bin/parser

@@ -0,0 +1,12 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  exec "$basedir/node"  "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
+else 
+  exec node  "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
+fi

+ 17 - 0
node_modules/.bin/parser.cmd

@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+  SET "_prog=%dp0%\node.exe"
+) ELSE (
+  SET "_prog=node"
+  SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\@babel\parser\bin\babel-parser.js" %*

+ 28 - 0
node_modules/.bin/parser.ps1

@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+  # Fix case when both the Windows and Linux builds of Node
+  # are installed in the same directory
+  $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "$basedir/node$exe"  "$basedir/../@babel/parser/bin/babel-parser.js" $args
+  } else {
+    & "$basedir/node$exe"  "$basedir/../@babel/parser/bin/babel-parser.js" $args
+  }
+  $ret=$LASTEXITCODE
+} else {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "node$exe"  "$basedir/../@babel/parser/bin/babel-parser.js" $args
+  } else {
+    & "node$exe"  "$basedir/../@babel/parser/bin/babel-parser.js" $args
+  }
+  $ret=$LASTEXITCODE
+}
+exit $ret

+ 12 - 0
node_modules/.bin/rollup

@@ -0,0 +1,12 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  exec "$basedir/node"  "$basedir/../rollup/dist/bin/rollup" "$@"
+else 
+  exec node  "$basedir/../rollup/dist/bin/rollup" "$@"
+fi

+ 17 - 0
node_modules/.bin/rollup.cmd

@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+  SET "_prog=%dp0%\node.exe"
+) ELSE (
+  SET "_prog=node"
+  SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\rollup\dist\bin\rollup" %*

+ 28 - 0
node_modules/.bin/rollup.ps1

@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+  # Fix case when both the Windows and Linux builds of Node
+  # are installed in the same directory
+  $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "$basedir/node$exe"  "$basedir/../rollup/dist/bin/rollup" $args
+  } else {
+    & "$basedir/node$exe"  "$basedir/../rollup/dist/bin/rollup" $args
+  }
+  $ret=$LASTEXITCODE
+} else {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "node$exe"  "$basedir/../rollup/dist/bin/rollup" $args
+  } else {
+    & "node$exe"  "$basedir/../rollup/dist/bin/rollup" $args
+  }
+  $ret=$LASTEXITCODE
+}
+exit $ret

+ 12 - 0
node_modules/.bin/vite

@@ -0,0 +1,12 @@
+#!/bin/sh
+basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
+
+case `uname` in
+    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  exec "$basedir/node"  "$basedir/../vite/bin/vite.js" "$@"
+else 
+  exec node  "$basedir/../vite/bin/vite.js" "$@"
+fi

+ 17 - 0
node_modules/.bin/vite.cmd

@@ -0,0 +1,17 @@
+@ECHO off
+GOTO start
+:find_dp0
+SET dp0=%~dp0
+EXIT /b
+:start
+SETLOCAL
+CALL :find_dp0
+
+IF EXIST "%dp0%\node.exe" (
+  SET "_prog=%dp0%\node.exe"
+) ELSE (
+  SET "_prog=node"
+  SET PATHEXT=%PATHEXT:;.JS;=;%
+)
+
+endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%"  "%dp0%\..\vite\bin\vite.js" %*

+ 28 - 0
node_modules/.bin/vite.ps1

@@ -0,0 +1,28 @@
+#!/usr/bin/env pwsh
+$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
+
+$exe=""
+if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
+  # Fix case when both the Windows and Linux builds of Node
+  # are installed in the same directory
+  $exe=".exe"
+}
+$ret=0
+if (Test-Path "$basedir/node$exe") {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "$basedir/node$exe"  "$basedir/../vite/bin/vite.js" $args
+  } else {
+    & "$basedir/node$exe"  "$basedir/../vite/bin/vite.js" $args
+  }
+  $ret=$LASTEXITCODE
+} else {
+  # Support pipeline input
+  if ($MyInvocation.ExpectingInput) {
+    $input | & "node$exe"  "$basedir/../vite/bin/vite.js" $args
+  } else {
+    & "node$exe"  "$basedir/../vite/bin/vite.js" $args
+  }
+  $ret=$LASTEXITCODE
+}
+exit $ret

+ 381 - 0
node_modules/.package-lock.json

@@ -0,0 +1,381 @@
+{
+  "name": "x11_template_vue",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+      "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+      "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz",
+      "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+      "dependencies": {
+        "@babel/types": "^7.29.8"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz",
+      "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+      "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+      "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
+      "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.0.0 || ^5.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.42.tgz",
+      "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==",
+      "dependencies": {
+        "@babel/parser": "^7.29.8",
+        "@vue/shared": "3.5.42",
+        "entities": "^7.0.1",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz",
+      "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
+      "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
+      "dependencies": {
+        "@babel/parser": "^7.29.8",
+        "@vue/compiler-core": "3.5.42",
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/compiler-ssr": "3.5.42",
+        "@vue/shared": "3.5.42",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.19",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz",
+      "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.42.tgz",
+      "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==",
+      "dependencies": {
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.42.tgz",
+      "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz",
+      "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.42",
+        "@vue/runtime-core": "3.5.42",
+        "@vue/shared": "3.5.42",
+        "csstype": "^3.2.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.42.tgz",
+      "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.42",
+        "@vue/runtime-dom": "3.5.42",
+        "@vue/shared": "3.5.42"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.42.tgz",
+      "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g=="
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
+    },
+    "node_modules/entities": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
+      "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.18.20",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.20.tgz",
+      "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.18.20",
+        "@esbuild/android-arm64": "0.18.20",
+        "@esbuild/android-x64": "0.18.20",
+        "@esbuild/darwin-arm64": "0.18.20",
+        "@esbuild/darwin-x64": "0.18.20",
+        "@esbuild/freebsd-arm64": "0.18.20",
+        "@esbuild/freebsd-x64": "0.18.20",
+        "@esbuild/linux-arm": "0.18.20",
+        "@esbuild/linux-arm64": "0.18.20",
+        "@esbuild/linux-ia32": "0.18.20",
+        "@esbuild/linux-loong64": "0.18.20",
+        "@esbuild/linux-mips64el": "0.18.20",
+        "@esbuild/linux-ppc64": "0.18.20",
+        "@esbuild/linux-riscv64": "0.18.20",
+        "@esbuild/linux-s390x": "0.18.20",
+        "@esbuild/linux-x64": "0.18.20",
+        "@esbuild/netbsd-x64": "0.18.20",
+        "@esbuild/openbsd-x64": "0.18.20",
+        "@esbuild/sunos-x64": "0.18.20",
+        "@esbuild/win32-arm64": "0.18.20",
+        "@esbuild/win32-ia32": "0.18.20",
+        "@esbuild/win32-x64": "0.18.20"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.18",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz",
+      "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+    },
+    "node_modules/postcss": {
+      "version": "8.5.28",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.28.tgz",
+      "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.18",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "3.30.0",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.30.0.tgz",
+      "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==",
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=14.18.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/vite": {
+      "version": "4.5.14",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-4.5.14.tgz",
+      "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
+      "dependencies": {
+        "esbuild": "^0.18.10",
+        "postcss": "^8.4.27",
+        "rollup": "^3.27.1"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      },
+      "peerDependencies": {
+        "@types/node": ">= 14",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.5.42",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.42.tgz",
+      "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.42",
+        "@vue/compiler-sfc": "3.5.42",
+        "@vue/runtime-dom": "3.5.42",
+        "@vue/server-renderer": "3.5.42",
+        "@vue/shared": "3.5.42"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    }
+  }
+}

+ 22 - 0
node_modules/@babel/helper-string-parser/LICENSE

@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2014-present Sebastian McKenzie and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 19 - 0
node_modules/@babel/helper-string-parser/README.md

@@ -0,0 +1,19 @@
+# @babel/helper-string-parser
+
+> A utility package to parse strings
+
+See our website [@babel/helper-string-parser](https://babeljs.io/docs/babel-helper-string-parser) for more information.
+
+## Install
+
+Using npm:
+
+```sh
+npm install --save @babel/helper-string-parser
+```
+
+or using yarn:
+
+```sh
+yarn add @babel/helper-string-parser
+```

+ 295 - 0
node_modules/@babel/helper-string-parser/lib/index.js

@@ -0,0 +1,295 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.readCodePoint = readCodePoint;
+exports.readInt = readInt;
+exports.readStringContents = readStringContents;
+var _isDigit = function isDigit(code) {
+  return code >= 48 && code <= 57;
+};
+const forbiddenNumericSeparatorSiblings = {
+  decBinOct: new Set([46, 66, 69, 79, 95, 98, 101, 111]),
+  hex: new Set([46, 88, 95, 120])
+};
+const isAllowedNumericSeparatorSibling = {
+  bin: ch => ch === 48 || ch === 49,
+  oct: ch => ch >= 48 && ch <= 55,
+  dec: ch => ch >= 48 && ch <= 57,
+  hex: ch => ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102
+};
+function readStringContents(type, input, pos, lineStart, curLine, errors) {
+  const initialPos = pos;
+  const initialLineStart = lineStart;
+  const initialCurLine = curLine;
+  let out = "";
+  let firstInvalidLoc = null;
+  let chunkStart = pos;
+  const {
+    length
+  } = input;
+  for (;;) {
+    if (pos >= length) {
+      errors.unterminated(initialPos, initialLineStart, initialCurLine);
+      out += input.slice(chunkStart, pos);
+      break;
+    }
+    const ch = input.charCodeAt(pos);
+    if (isStringEnd(type, ch, input, pos)) {
+      out += input.slice(chunkStart, pos);
+      break;
+    }
+    if (ch === 92) {
+      out += input.slice(chunkStart, pos);
+      const res = readEscapedChar(input, pos, lineStart, curLine, type === "template", errors);
+      if (res.ch === null && !firstInvalidLoc) {
+        firstInvalidLoc = {
+          pos,
+          lineStart,
+          curLine
+        };
+      } else {
+        out += res.ch;
+      }
+      ({
+        pos,
+        lineStart,
+        curLine
+      } = res);
+      chunkStart = pos;
+    } else if (ch === 8232 || ch === 8233) {
+      ++pos;
+      ++curLine;
+      lineStart = pos;
+    } else if (ch === 10 || ch === 13) {
+      if (type === "template") {
+        out += input.slice(chunkStart, pos) + "\n";
+        ++pos;
+        if (ch === 13 && input.charCodeAt(pos) === 10) {
+          ++pos;
+        }
+        ++curLine;
+        chunkStart = lineStart = pos;
+      } else {
+        errors.unterminated(initialPos, initialLineStart, initialCurLine);
+      }
+    } else {
+      ++pos;
+    }
+  }
+  return {
+    pos,
+    str: out,
+    firstInvalidLoc,
+    lineStart,
+    curLine,
+    containsInvalid: !!firstInvalidLoc
+  };
+}
+function isStringEnd(type, ch, input, pos) {
+  if (type === "template") {
+    return ch === 96 || ch === 36 && input.charCodeAt(pos + 1) === 123;
+  }
+  return ch === (type === "double" ? 34 : 39);
+}
+function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) {
+  const throwOnInvalid = !inTemplate;
+  pos++;
+  const res = ch => ({
+    pos,
+    ch,
+    lineStart,
+    curLine
+  });
+  const ch = input.charCodeAt(pos++);
+  switch (ch) {
+    case 110:
+      return res("\n");
+    case 114:
+      return res("\r");
+    case 120:
+      {
+        let code;
+        ({
+          code,
+          pos
+        } = readHexChar(input, pos, lineStart, curLine, 2, false, throwOnInvalid, errors));
+        return res(code === null ? null : String.fromCharCode(code));
+      }
+    case 117:
+      {
+        let code;
+        ({
+          code,
+          pos
+        } = readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors));
+        return res(code === null ? null : String.fromCodePoint(code));
+      }
+    case 116:
+      return res("\t");
+    case 98:
+      return res("\b");
+    case 118:
+      return res("\u000b");
+    case 102:
+      return res("\f");
+    case 13:
+      if (input.charCodeAt(pos) === 10) {
+        ++pos;
+      }
+    case 10:
+      lineStart = pos;
+      ++curLine;
+    case 8232:
+    case 8233:
+      return res("");
+    case 56:
+    case 57:
+      if (inTemplate) {
+        return res(null);
+      } else {
+        errors.strictNumericEscape(pos - 1, lineStart, curLine);
+      }
+    default:
+      if (ch >= 48 && ch <= 55) {
+        const startPos = pos - 1;
+        const match = /^[0-7]+/.exec(input.slice(startPos, pos + 2));
+        let octalStr = match[0];
+        let octal = parseInt(octalStr, 8);
+        if (octal > 255) {
+          octalStr = octalStr.slice(0, -1);
+          octal = parseInt(octalStr, 8);
+        }
+        pos += octalStr.length - 1;
+        const next = input.charCodeAt(pos);
+        if (octalStr !== "0" || next === 56 || next === 57) {
+          if (inTemplate) {
+            return res(null);
+          } else {
+            errors.strictNumericEscape(startPos, lineStart, curLine);
+          }
+        }
+        return res(String.fromCharCode(octal));
+      }
+      return res(String.fromCharCode(ch));
+  }
+}
+function readHexChar(input, pos, lineStart, curLine, len, forceLen, throwOnInvalid, errors) {
+  const initialPos = pos;
+  let n;
+  ({
+    n,
+    pos
+  } = readInt(input, pos, lineStart, curLine, 16, len, forceLen, false, errors, !throwOnInvalid));
+  if (n === null) {
+    if (throwOnInvalid) {
+      errors.invalidEscapeSequence(initialPos, lineStart, curLine);
+    } else {
+      pos = initialPos - 1;
+    }
+  }
+  return {
+    code: n,
+    pos
+  };
+}
+function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumSeparator, errors, bailOnError) {
+  const start = pos;
+  const forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
+  const isAllowedSibling = radix === 16 ? isAllowedNumericSeparatorSibling.hex : radix === 10 ? isAllowedNumericSeparatorSibling.dec : radix === 8 ? isAllowedNumericSeparatorSibling.oct : isAllowedNumericSeparatorSibling.bin;
+  let invalid = false;
+  let total = 0;
+  for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
+    const code = input.charCodeAt(pos);
+    let val;
+    if (code === 95 && allowNumSeparator !== "bail") {
+      const prev = input.charCodeAt(pos - 1);
+      const next = input.charCodeAt(pos + 1);
+      if (!allowNumSeparator) {
+        if (bailOnError) return {
+          n: null,
+          pos
+        };
+        errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
+      } else if (Number.isNaN(next) || !isAllowedSibling(next) || forbiddenSiblings.has(prev) || forbiddenSiblings.has(next)) {
+        if (bailOnError) return {
+          n: null,
+          pos
+        };
+        errors.unexpectedNumericSeparator(pos, lineStart, curLine);
+      }
+      ++pos;
+      continue;
+    }
+    if (code >= 97) {
+      val = code - 97 + 10;
+    } else if (code >= 65) {
+      val = code - 65 + 10;
+    } else if (_isDigit(code)) {
+      val = code - 48;
+    } else {
+      val = Infinity;
+    }
+    if (val >= radix) {
+      if (val <= 9 && bailOnError) {
+        return {
+          n: null,
+          pos
+        };
+      } else if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
+        val = 0;
+      } else if (forceLen) {
+        val = 0;
+        invalid = true;
+      } else {
+        break;
+      }
+    }
+    ++pos;
+    total = total * radix + val;
+  }
+  if (pos === start || len != null && pos - start !== len || invalid) {
+    return {
+      n: null,
+      pos
+    };
+  }
+  return {
+    n: total,
+    pos
+  };
+}
+function readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors) {
+  const ch = input.charCodeAt(pos);
+  let code;
+  if (ch === 123) {
+    ++pos;
+    ({
+      code,
+      pos
+    } = readHexChar(input, pos, lineStart, curLine, input.indexOf("}", pos) - pos, true, throwOnInvalid, errors));
+    ++pos;
+    if (code !== null && code > 0x10ffff) {
+      if (throwOnInvalid) {
+        errors.invalidCodePoint(pos, lineStart, curLine);
+      } else {
+        return {
+          code: null,
+          pos
+        };
+      }
+    }
+  } else {
+    ({
+      code,
+      pos
+    } = readHexChar(input, pos, lineStart, curLine, 4, false, throwOnInvalid, errors));
+  }
+  return {
+    code,
+    pos
+  };
+}
+
+//# sourceMappingURL=index.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/helper-string-parser/lib/index.js.map


+ 31 - 0
node_modules/@babel/helper-string-parser/package.json

@@ -0,0 +1,31 @@
+{
+  "name": "@babel/helper-string-parser",
+  "version": "7.29.7",
+  "description": "A utility package to parse strings",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/babel/babel.git",
+    "directory": "packages/babel-helper-string-parser"
+  },
+  "homepage": "https://babel.dev/docs/en/next/babel-helper-string-parser",
+  "license": "MIT",
+  "publishConfig": {
+    "access": "public"
+  },
+  "main": "./lib/index.js",
+  "devDependencies": {
+    "charcodes": "^0.2.0"
+  },
+  "engines": {
+    "node": ">=6.9.0"
+  },
+  "author": "The Babel Team (https://babel.dev/team)",
+  "exports": {
+    ".": {
+      "types": "./lib/index.d.ts",
+      "default": "./lib/index.js"
+    },
+    "./package.json": "./package.json"
+  },
+  "type": "commonjs"
+}

+ 22 - 0
node_modules/@babel/helper-validator-identifier/LICENSE

@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2014-present Sebastian McKenzie and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 19 - 0
node_modules/@babel/helper-validator-identifier/README.md

@@ -0,0 +1,19 @@
+# @babel/helper-validator-identifier
+
+> Validate identifier/keywords name
+
+See our website [@babel/helper-validator-identifier](https://babeljs.io/docs/babel-helper-validator-identifier) for more information.
+
+## Install
+
+Using npm:
+
+```sh
+npm install --save @babel/helper-validator-identifier
+```
+
+or using yarn:
+
+```sh
+yarn add @babel/helper-validator-identifier
+```

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 8 - 0
node_modules/@babel/helper-validator-identifier/lib/identifier.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/helper-validator-identifier/lib/identifier.js.map


+ 57 - 0
node_modules/@babel/helper-validator-identifier/lib/index.js

@@ -0,0 +1,57 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+Object.defineProperty(exports, "isIdentifierChar", {
+  enumerable: true,
+  get: function () {
+    return _identifier.isIdentifierChar;
+  }
+});
+Object.defineProperty(exports, "isIdentifierName", {
+  enumerable: true,
+  get: function () {
+    return _identifier.isIdentifierName;
+  }
+});
+Object.defineProperty(exports, "isIdentifierStart", {
+  enumerable: true,
+  get: function () {
+    return _identifier.isIdentifierStart;
+  }
+});
+Object.defineProperty(exports, "isKeyword", {
+  enumerable: true,
+  get: function () {
+    return _keyword.isKeyword;
+  }
+});
+Object.defineProperty(exports, "isReservedWord", {
+  enumerable: true,
+  get: function () {
+    return _keyword.isReservedWord;
+  }
+});
+Object.defineProperty(exports, "isStrictBindOnlyReservedWord", {
+  enumerable: true,
+  get: function () {
+    return _keyword.isStrictBindOnlyReservedWord;
+  }
+});
+Object.defineProperty(exports, "isStrictBindReservedWord", {
+  enumerable: true,
+  get: function () {
+    return _keyword.isStrictBindReservedWord;
+  }
+});
+Object.defineProperty(exports, "isStrictReservedWord", {
+  enumerable: true,
+  get: function () {
+    return _keyword.isStrictReservedWord;
+  }
+});
+var _identifier = require("./identifier.js");
+var _keyword = require("./keyword.js");
+
+//# sourceMappingURL=index.js.map

+ 1 - 0
node_modules/@babel/helper-validator-identifier/lib/index.js.map

@@ -0,0 +1 @@
+{"version":3,"names":["_identifier","require","_keyword"],"sources":["../src/index.ts"],"sourcesContent":["export {\n  isIdentifierName,\n  isIdentifierChar,\n  isIdentifierStart,\n} from \"./identifier.ts\";\nexport {\n  isReservedWord,\n  isStrictBindOnlyReservedWord,\n  isStrictBindReservedWord,\n  isStrictReservedWord,\n  isKeyword,\n} from \"./keyword.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAKA,IAAAC,QAAA,GAAAD,OAAA","ignoreList":[]}

+ 35 - 0
node_modules/@babel/helper-validator-identifier/lib/keyword.js

@@ -0,0 +1,35 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.isKeyword = isKeyword;
+exports.isReservedWord = isReservedWord;
+exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord;
+exports.isStrictBindReservedWord = isStrictBindReservedWord;
+exports.isStrictReservedWord = isStrictReservedWord;
+const reservedWords = {
+  keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"],
+  strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"],
+  strictBind: ["eval", "arguments"]
+};
+const keywords = new Set(reservedWords.keyword);
+const reservedWordsStrictSet = new Set(reservedWords.strict);
+const reservedWordsStrictBindSet = new Set(reservedWords.strictBind);
+function isReservedWord(word, inModule) {
+  return inModule && word === "await" || word === "enum";
+}
+function isStrictReservedWord(word, inModule) {
+  return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);
+}
+function isStrictBindOnlyReservedWord(word) {
+  return reservedWordsStrictBindSet.has(word);
+}
+function isStrictBindReservedWord(word, inModule) {
+  return isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word);
+}
+function isKeyword(word) {
+  return keywords.has(word);
+}
+
+//# sourceMappingURL=keyword.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/helper-validator-identifier/lib/keyword.js.map


+ 31 - 0
node_modules/@babel/helper-validator-identifier/package.json

@@ -0,0 +1,31 @@
+{
+  "name": "@babel/helper-validator-identifier",
+  "version": "7.29.7",
+  "description": "Validate identifier/keywords name",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/babel/babel.git",
+    "directory": "packages/babel-helper-validator-identifier"
+  },
+  "license": "MIT",
+  "publishConfig": {
+    "access": "public"
+  },
+  "main": "./lib/index.js",
+  "exports": {
+    ".": {
+      "types": "./lib/index.d.ts",
+      "default": "./lib/index.js"
+    },
+    "./package.json": "./package.json"
+  },
+  "devDependencies": {
+    "@unicode/unicode-17.0.0": "^1.6.10",
+    "charcodes": "^0.2.0"
+  },
+  "engines": {
+    "node": ">=6.9.0"
+  },
+  "author": "The Babel Team (https://babel.dev/team)",
+  "type": "commonjs"
+}

+ 1073 - 0
node_modules/@babel/parser/CHANGELOG.md

@@ -0,0 +1,1073 @@
+# Changelog
+
+> **Tags:**
+> - :boom:       [Breaking Change]
+> - :eyeglasses: [Spec Compliance]
+> - :rocket:     [New Feature]
+> - :bug:        [Bug Fix]
+> - :memo:       [Documentation]
+> - :house:      [Internal]
+> - :nail_care:  [Polish]
+
+> Semver Policy: https://github.com/babel/babel/tree/main/packages/babel-parser#semver
+
+_Note: Gaps between patch versions are faulty, broken or test releases._
+
+See the [Babel Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) for the pre-6.8.0 version Changelog.
+
+## 6.17.1 (2017-05-10)
+
+### :bug: Bug Fix
+ * Fix typo in flow spread operator error (Brian Ng)
+ * Fixed invalid number literal parsing ([#473](https://github.com/babel/babylon/pull/473)) (Alex Kuzmenko)
+ * Fix number parser ([#433](https://github.com/babel/babylon/pull/433)) (Alex Kuzmenko)
+ * Ensure non pattern shorthand props are checked for reserved words ([#479](https://github.com/babel/babylon/pull/479)) (Brian Ng)
+ * Remove jsx context when parsing arrow functions ([#475](https://github.com/babel/babylon/pull/475)) (Brian Ng)
+ * Allow super in class properties ([#499](https://github.com/babel/babylon/pull/499)) (Brian Ng)
+ * Allow flow class field to be named constructor ([#510](https://github.com/babel/babylon/pull/510)) (Brian Ng)
+
+## 6.17.0 (2017-04-20)
+
+### :bug: Bug Fix
+ * Cherry-pick #418 to 6.x ([#476](https://github.com/babel/babylon/pull/476)) (Sebastian McKenzie)
+ * Add support for invalid escapes in tagged templates ([#274](https://github.com/babel/babylon/pull/274)) (Kevin Gibbons)
+ * Throw error if new.target is used outside of a function ([#402](https://github.com/babel/babylon/pull/402)) (Brian Ng)
+ * Fix parsing of class properties ([#351](https://github.com/babel/babylon/pull/351)) (Kevin Gibbons)
+ * Fix parsing yield with dynamicImport ([#383](https://github.com/babel/babylon/pull/383)) (Brian Ng)
+ * Ensure consistent start args for parseParenItem ([#386](https://github.com/babel/babylon/pull/386)) (Brian Ng)
+
+## 7.0.0-beta.8 (2017-04-04)
+
+### New Feature
+* Add support for flow type spread (#418) (Conrad Buck)
+* Allow statics in flow interfaces (#427) (Brian Ng)
+
+### Bug Fix
+* Fix predicate attachment to match flow parser (#428) (Brian Ng)
+* Add extra.raw back to JSXText and JSXAttribute (#344) (Alex Rattray)
+* Fix rest parameters with array and objects (#424) (Brian Ng)
+* Fix number parser (#433) (Alex Kuzmenko)
+
+### Docs
+* Fix CONTRIBUTING.md [skip ci] (#432) (Alex Kuzmenko)
+
+### Internal
+* Use babel-register script when running babel smoke tests (#442) (Brian Ng)
+
+## 7.0.0-beta.7 (2017-03-22)
+
+### Spec Compliance
+* Remove babylon plugin for template revision since it's stage-4 (#426) (Henry Zhu)
+
+### Bug Fix
+
+* Fix push-pop logic in flow (#405) (Daniel Tschinder)
+
+## 7.0.0-beta.6 (2017-03-21)
+
+### New Feature
+* Add support for invalid escapes in tagged templates (#274) (Kevin Gibbons)
+
+### Polish
+* Improves error message when super is called outside of constructor (#408) (Arshabh Kumar Agarwal)
+
+### Docs
+
+* [7.0] Moved value field in spec from ObjectMember to ObjectProperty as ObjectMethod's don't have it (#415) [skip ci] (James Browning)
+
+## 7.0.0-beta.5 (2017-03-21)
+
+### Bug Fix
+* Throw error if new.target is used outside of a function (#402) (Brian Ng)
+* Fix parsing of class properties (#351) (Kevin Gibbons)
+
+### Other
+ * Test runner: Detect extra property in 'actual' but not in 'expected'. (#407) (Andy)
+ * Optimize travis builds (#419) (Daniel Tschinder)
+ * Update codecov to 2.0 (#412) (Daniel Tschinder)
+ * Fix spec for ClassMethod: It doesn't have a function, it *is* a function. (#406) [skip ci] (Andy)
+ * Changed Non-existent RestPattern to RestElement which is what is actually parsed (#409) [skip ci] (James Browning)
+ * Upgrade flow to 0.41 (Daniel Tschinder)
+ * Fix watch command (#403) (Brian Ng)
+ * Update yarn lock (Daniel Tschinder)
+ * Fix watch command (#403) (Brian Ng)
+ * chore(package): update flow-bin to version 0.41.0 (#395) (greenkeeper[bot])
+ * Add estree test for correct order of directives (Daniel Tschinder)
+ * Add DoExpression to spec (#364) (Alex Kuzmenko)
+ * Mention cloning of repository in CONTRIBUTING.md (#391) [skip ci] (Sumedh Nimkarde)
+ * Explain how to run only one test (#389) [skip ci] (Aaron Ang)
+
+ ## 7.0.0-beta.4 (2017-03-01)
+
+* Don't consume async when checking for async func decl (#377) (Brian Ng)
+* add `ranges` option [skip ci] (Henry Zhu)
+* Don't parse class properties without initializers when classProperties is disabled and Flow is enabled (#300) (Andrew Levine)
+
+## 7.0.0-beta.3 (2017-02-28)
+
+- [7.0] Change RestProperty/SpreadProperty to RestElement/SpreadElement (#384)
+- Merge changes from 6.x
+
+## 7.0.0-beta.2 (2017-02-20)
+
+- estree: correctly change literals in all cases (#368) (Daniel Tschinder)
+
+## 7.0.0-beta.1 (2017-02-20)
+
+- Fix negative number literal typeannotations (#366) (Daniel Tschinder)
+- Update contributing with more test info [skip ci] (#355) (Brian Ng)
+
+## 7.0.0-beta.0 (2017-02-15)
+
+- Reintroduce Variance node (#333) (Daniel Tschinder)
+- Rename NumericLiteralTypeAnnotation to NumberLiteralTypeAnnotation (#332) (Charles Pick)
+- [7.0] Remove ForAwaitStatement, add await flag to ForOfStatement (#349) (Brandon Dail)
+- chore(package): update ava to version 0.18.0 (#345) (greenkeeper[bot])
+- chore(package): update babel-plugin-istanbul to version 4.0.0 (#350) (greenkeeper[bot])
+- Change location of ObjectTypeIndexer to match flow (#228) (Daniel Tschinder)
+- Rename flow AST Type ExistentialTypeParam to ExistsTypeAnnotation (#322) (Toru Kobayashi)
+- Revert "Temporary rollback for erroring on trailing comma with spread (#154)" (#290) (Daniel Tschinder)
+- Remove classConstructorCall plugin (#291) (Brian Ng)
+- Update yarn.lock (Daniel Tschinder)
+- Update cross-env to 3.x (Daniel Tschinder)
+- [7.0] Remove node 0.10, 0.12 and 5 from Travis (#284) (Sergey Rubanov)
+- Remove `String.fromCodePoint` shim (#279) (Mathias Bynens)
+
+## 6.16.1 (2017-02-23)
+
+### :bug: Regression
+
+- Revert "Fix export default async function to be FunctionDeclaration" ([#375](https://github.com/babel/babylon/pull/375))
+
+Need to modify Babel for this AST node change, so moving to 7.0.
+
+- Revert "Don't parse class properties without initializers when classProperties plugin is disabled, and Flow is enabled" ([#376](https://github.com/babel/babylon/pull/376))
+
+[react-native](https://github.com/facebook/react-native/issues/12542) broke with this so we reverted.
+
+## 6.16.0 (2017-02-23)
+
+### :rocket: New Feature
+
+***ESTree*** compatibility as plugin ([#277](https://github.com/babel/babylon/pull/277)) (Daniel Tschinder)
+
+We finally introduce a new compatibility layer for ESTree. To put babylon into ESTree-compatible mode the new plugin `estree` can be enabled. In this mode the parser will output an AST that is compliant to the specs of [ESTree](https://github.com/estree/estree/)
+
+We highly recommend everyone who uses babylon outside of babel to use this plugin. This will make it much easier for users to switch between different ESTree-compatible parsers. We so far tested several projects with different parsers and exchanged their parser to babylon and in nearly all cases it worked out of the box. Some other estree-compatible parsers include `acorn`, `esprima`, `espree`, `flow-parser`, etc.
+
+To enable `estree` mode simply add the plugin in the config:
+```json
+{
+  "plugins": [ "estree" ]
+}
+```
+
+If you want to migrate your project from non-ESTree mode to ESTree, have a look at our [Readme](https://github.com/babel/babylon/#output), where all deviations are mentioned.
+
+Add a parseExpression public method ([#213](https://github.com/babel/babylon/pull/213)) (jeromew)
+
+Babylon exports a new function to parse a single expression
+
+```js
+import { parseExpression } from 'babylon';
+
+const ast = parseExpression('x || y && z', options);
+```
+
+The returned AST will only consist of the expression. The options are the same as for `parse()`
+
+Add startLine option ([#346](https://github.com/babel/babylon/pull/346)) (Raphael Mu)
+
+A new option was added to babylon allowing to change the initial linenumber for the first line which is usually `1`.
+Changing this for example to `100` will make line `1` of the input source to be marked as line `100`, line `2` as `101`, line `3` as `102`, ...
+
+Function predicate declaration ([#103](https://github.com/babel/babylon/pull/103)) (Panagiotis Vekris)
+
+Added support for function predicates which flow introduced in version 0.33.0
+
+```js
+declare function is_number(x: mixed): boolean %checks(typeof x === "number");
+```
+
+Allow imports in declare module ([#315](https://github.com/babel/babylon/pull/315)) (Daniel Tschinder)
+
+Added support for imports within module declarations which flow introduced in version 0.37.0
+
+```js
+declare module "C" {
+  import type { DT } from "D";
+  declare export type CT = { D: DT };
+}
+```
+
+### :eyeglasses: Spec Compliance
+
+Forbid semicolons after decorators in classes ([#352](https://github.com/babel/babylon/pull/352)) (Kevin Gibbons)
+
+This example now correctly throws an error when there is a semicolon after the decorator:
+
+```js
+class A {
+@a;
+foo(){}
+}
+```
+
+Keywords are not allowed as local specifier ([#307](https://github.com/babel/babylon/pull/307)) (Daniel Tschinder)
+
+Using keywords in imports is not allowed anymore:
+
+```js
+import { default } from "foo";
+import { a as debugger } from "foo";
+```
+
+Do not allow overwritting of primitive types ([#314](https://github.com/babel/babylon/pull/314)) (Daniel Tschinder)
+
+In flow it is now forbidden to overwrite the primitive types `"any"`, `"mixed"`, `"empty"`, `"bool"`, `"boolean"`, `"number"`, `"string"`, `"void"` and `"null"` with your own type declaration.
+
+Disallow import type { type a } from … ([#305](https://github.com/babel/babylon/pull/305)) (Daniel Tschinder)
+
+The following code now correctly throws an error
+
+```js
+import type { type a } from "foo";
+```
+
+Don't parse class properties without initializers when classProperties is disabled and Flow is enabled ([#300](https://github.com/babel/babylon/pull/300)) (Andrew Levine)
+
+Ensure that you enable the `classProperties` plugin in order to enable correct parsing of class properties. Prior to this version it was possible to parse them by enabling the `flow` plugin but this was not intended the behaviour.
+
+If you enable the flow plugin you can only define the type of the class properties, but not initialize them.
+
+Fix export default async function to be FunctionDeclaration ([#324](https://github.com/babel/babylon/pull/324)) (Daniel Tschinder)
+
+Parsing the following code now returns a `FunctionDeclaration` AST node instead of `FunctionExpression`.
+
+```js
+export default async function bar() {};
+```
+
+### :nail_care: Polish
+
+Improve error message on attempt to destructure named import ([#288](https://github.com/babel/babylon/pull/288)) (Brian Ng)
+
+### :bug: Bug Fix
+
+Fix negative number literal typeannotations ([#366](https://github.com/babel/babylon/pull/366)) (Daniel Tschinder)
+
+Ensure takeDecorators is called on exported class ([#358](https://github.com/babel/babylon/pull/358)) (Brian Ng)
+
+ESTree: correctly change literals in all cases ([#368](https://github.com/babel/babylon/pull/368)) (Daniel Tschinder)
+
+Correctly convert RestProperty to Assignable ([#339](https://github.com/babel/babylon/pull/339)) (Daniel Tschinder)
+
+Fix #321 by allowing question marks in type params ([#338](https://github.com/babel/babylon/pull/338)) (Daniel Tschinder)
+
+Fix #336 by correctly setting arrow-param ([#337](https://github.com/babel/babylon/pull/337)) (Daniel Tschinder)
+
+Fix parse error when destructuring `set` with default value ([#317](https://github.com/babel/babylon/pull/317)) (Brian Ng)
+
+Fix ObjectTypeCallProperty static ([#298](https://github.com/babel/babylon/pull/298)) (Dan Harper)
+
+
+### :house: Internal
+
+Fix generator-method-with-computed-name spec ([#360](https://github.com/babel/babylon/pull/360)) (Alex Rattray)
+
+Fix flow type-parameter-declaration test with unintended semantic ([#361](https://github.com/babel/babylon/pull/361)) (Alex Rattray)
+
+Cleanup and splitup parser functions ([#295](https://github.com/babel/babylon/pull/295)) (Daniel Tschinder)
+
+chore(package): update flow-bin to version 0.38.0 ([#313](https://github.com/babel/babylon/pull/313)) (greenkeeper[bot])
+
+Call inner function instead of 1:1 copy to plugin ([#294](https://github.com/babel/babylon/pull/294)) (Daniel Tschinder)
+
+Update eslint-config-babel to the latest version 🚀 ([#299](https://github.com/babel/babylon/pull/299)) (greenkeeper[bot])
+
+Update eslint-config-babel to the latest version 🚀 ([#293](https://github.com/babel/babylon/pull/293)) (greenkeeper[bot])
+
+devDeps: remove eslint-plugin-babel ([#292](https://github.com/babel/babylon/pull/292)) (Kai Cataldo)
+
+Correct indent eslint rule config ([#276](https://github.com/babel/babylon/pull/276)) (Daniel Tschinder)
+
+Fail tests that have expected.json and throws-option ([#285](https://github.com/babel/babylon/pull/285)) (Daniel Tschinder)
+
+### :memo: Documentation
+
+Update contributing with more test info [skip ci] ([#355](https://github.com/babel/babylon/pull/355)) (Brian Ng)
+
+Update API documentation ([#330](https://github.com/babel/babylon/pull/330)) (Timothy Gu)
+
+Added keywords to package.json ([#323](https://github.com/babel/babylon/pull/323)) (Dmytro)
+
+AST spec: fix casing of `RegExpLiteral` ([#318](https://github.com/babel/babylon/pull/318)) (Mathias Bynens)
+
+## 6.15.0 (2017-01-10)
+
+### :eyeglasses: Spec Compliance
+
+Add support for Flow shorthand import type ([#267](https://github.com/babel/babylon/pull/267)) (Jeff Morrison)
+
+This change implements flows new shorthand import syntax
+and where previously you had to write this code:
+
+```js
+import {someValue} from "blah";
+import type {someType} from "blah";
+import typeof {someOtherValue} from "blah";
+```
+
+you can now write it like this:
+
+```js
+import {
+  someValue,
+  type someType,
+  typeof someOtherValue,
+} from "blah";
+```
+
+For more information look at [this](https://github.com/facebook/flow/pull/2890) pull request.
+
+flow: allow leading pipes in all positions ([#256](https://github.com/babel/babylon/pull/256)) (Vladimir Kurchatkin)
+
+This change now allows a leading pipe everywhere types can be used:
+```js
+var f = (x): | 1 | 2 => 1;
+```
+
+Throw error when exporting non-declaration ([#241](https://github.com/babel/babylon/pull/241)) (Kai Cataldo)
+
+Previously babylon parsed the following exports, although they are not valid:
+```js
+export typeof foo;
+export new Foo();
+export function() {};
+export for (;;);
+export while(foo);
+```
+
+### :bug: Bug Fix
+
+Don't set inType flag when parsing property names ([#266](https://github.com/babel/babylon/pull/266)) (Vladimir Kurchatkin)
+
+This fixes parsing of this case:
+
+```js
+const map = {
+  [age <= 17] : 'Too young'
+};
+```
+
+Fix source location for JSXEmptyExpression nodes (fixes #248) ([#249](https://github.com/babel/babylon/pull/249)) (James Long)
+
+The following case produced an invalid AST
+```js
+<div>{/* foo */}</div>
+```
+
+Use fromCodePoint to convert high value unicode entities ([#243](https://github.com/babel/babylon/pull/243)) (Ryan Duffy)
+
+When high value unicode entities (e.g. 💩) were used in the input source code they are now correctly encoded in the resulting AST.
+
+Rename folder to avoid Windows-illegal characters ([#281](https://github.com/babel/babylon/pull/281)) (Ryan Plant)
+
+Allow this.state.clone() when parsing decorators ([#262](https://github.com/babel/babylon/pull/262)) (Alex Rattray)
+
+### :house: Internal
+
+User external-helpers ([#254](https://github.com/babel/babylon/pull/254)) (Daniel Tschinder)
+
+Add watch script for dev ([#234](https://github.com/babel/babylon/pull/234)) (Kai Cataldo)
+
+Freeze current plugins list for "*" option, and remove from README.md ([#245](https://github.com/babel/babylon/pull/245)) (Andrew Levine)
+
+Prepare tests for multiple fixture runners. ([#240](https://github.com/babel/babylon/pull/240)) (Daniel Tschinder)
+
+Add some test coverage for decorators stage-0 plugin ([#250](https://github.com/babel/babylon/pull/250)) (Andrew Levine)
+
+Refactor tokenizer types file ([#263](https://github.com/babel/babylon/pull/263)) (Sven SAULEAU)
+
+Update eslint-config-babel to the latest version 🚀 ([#273](https://github.com/babel/babylon/pull/273)) (greenkeeper[bot])
+
+chore(package): update rollup to version 0.41.0 ([#272](https://github.com/babel/babylon/pull/272)) (greenkeeper[bot])
+
+chore(package): update flow-bin to version 0.37.0 ([#255](https://github.com/babel/babylon/pull/255)) (greenkeeper[bot])
+
+## 6.14.1 (2016-11-17)
+
+### :bug: Bug Fix
+
+Allow `"plugins": ["*"]` ([#229](https://github.com/babel/babylon/pull/229)) (Daniel Tschinder)
+
+```js
+{
+  "plugins": ["*"]
+}
+```
+
+Will include all parser plugins instead of specifying each one individually. Useful for tools like babel-eslint, jscodeshift, and ast-explorer.
+
+## 6.14.0 (2016-11-16)
+
+### :eyeglasses: Spec Compliance
+
+Throw error for reserved words `enum` and `await` ([#195](https://github.com/babel/babylon/pull/195)) (Kai Cataldo)
+
+[11.6.2.2 Future Reserved Words](http://www.ecma-international.org/ecma-262/6.0/#sec-future-reserved-words)
+
+Babylon will throw for more reserved words such as `enum` or `await` (in strict mode).
+
+```
+class enum {} // throws
+class await {} // throws in strict mode (module)
+```
+
+Optional names for function types and object type indexers ([#197](https://github.com/babel/babylon/pull/197)) (Gabe Levi)
+
+So where you used to have to write
+
+```js
+type A = (x: string, y: boolean) => number;
+type B = (z: string) => number;
+type C = { [key: string]: number };
+```
+
+you can now write (with flow 0.34.0)
+
+```js
+type A = (string, boolean) => number;
+type B = string => number;
+type C = { [string]: number };
+```
+
+Parse flow nested array type annotations like `number[][]` ([#219](https://github.com/babel/babylon/pull/219)) (Bernhard Häussner)
+
+Supports these form now of specifying array types:
+
+```js
+var a: number[][][][];
+var b: string[][];
+```
+
+### :bug: Bug Fix
+
+Correctly eat semicolon at the end of `DelcareModuleExports` ([#223](https://github.com/babel/babylon/pull/223))  (Daniel Tschinder)
+
+```
+declare module "foo" { declare module.exports: number }
+declare module "foo" { declare module.exports: number; }  // also allowed now
+```
+
+### :house: Internal
+
+ * Count Babel tests towards Babylon code coverage ([#182](https://github.com/babel/babylon/pull/182)) (Moti Zilberman)
+ * Fix strange line endings ([#214](https://github.com/babel/babylon/pull/214)) (Thomas Grainger)
+ * Add node 7 (Daniel Tschinder)
+ * chore(package): update flow-bin to version 0.34.0 ([#204](https://github.com/babel/babylon/pull/204)) (Greenkeeper)
+
+## v6.13.1 (2016-10-26)
+
+### :nail_care: Polish
+
+- Use rollup for bundling to speed up startup time ([#190](https://github.com/babel/babylon/pull/190)) ([@drewml](https://github.com/DrewML))
+
+```js
+const babylon = require('babylon');
+const ast = babylon.parse('var foo = "lol";');
+```
+
+With that test case, there was a ~95ms savings by removing the need for node to build/traverse the dependency graph.
+
+**Without bundling**
+![image](https://cloud.githubusercontent.com/assets/5233399/19420264/3133497e-93ad-11e6-9a6a-2da59c4f5c13.png)
+
+**With bundling**
+![image](https://cloud.githubusercontent.com/assets/5233399/19420267/388f556e-93ad-11e6-813e-7c5c396be322.png)
+
+- add clean command [skip ci] ([#201](https://github.com/babel/babylon/pull/201)) (Henry Zhu)
+- add ForAwaitStatement (async generator already added) [skip ci] ([#196](https://github.com/babel/babylon/pull/196)) (Henry Zhu)
+
+## v6.13.0 (2016-10-21)
+
+### :eyeglasses: Spec Compliance
+
+Property variance type annotations for Flow plugin ([#161](https://github.com/babel/babylon/pull/161)) (Sam Goldman)
+
+> See https://flowtype.org/docs/variance.html for more information
+
+```js
+type T = { +p: T };
+interface T { -p: T };
+declare class T { +[k:K]: V };
+class T { -[k:K]: V };
+class C2 { +p: T = e };
+```
+
+Raise error on duplicate definition of __proto__ ([#183](https://github.com/babel/babylon/pull/183)) (Moti Zilberman)
+
+```js
+({ __proto__: 1, __proto__: 2 }) // Throws an error now
+```
+
+### :bug: Bug Fix
+
+Flow: Allow class properties to be named `static` ([#184](https://github.com/babel/babylon/pull/184)) (Moti Zilberman)
+
+```js
+declare class A {
+  static: T;
+}
+```
+
+Allow "async" as identifier for object literal property shorthand ([#187](https://github.com/babel/babylon/pull/187)) (Andrew Levine)
+
+```js
+var foo = { async, bar };
+```
+
+### :nail_care: Polish
+
+Fix flowtype and add inType to state ([#189](https://github.com/babel/babylon/pull/189)) (Daniel Tschinder)
+
+> This improves the performance slightly (because of hidden classes)
+
+### :house: Internal
+
+Fix .gitattributes line ending setting ([#191](https://github.com/babel/babylon/pull/191)) (Moti Zilberman)
+
+Increase test coverage ([#175](https://github.com/babel/babylon/pull/175) (Moti Zilberman)
+
+Readd missin .eslinignore for IDEs (Daniel Tschinder)
+
+Error on missing expected.json fixture in CI ([#188](https://github.com/babel/babylon/pull/188)) (Moti Zilberman)
+
+Add .gitattributes and .editorconfig for LF line endings ([#179](https://github.com/babel/babylon/pull/179)) (Moti Zilberman)
+
+Fixes two tests that are failing after the merge of #172 ([#177](https://github.com/babel/babylon/pull/177)) (Moti Zilberman)
+
+## v6.12.0 (2016-10-14)
+
+### :eyeglasses: Spec Compliance
+
+Implement import() syntax ([#163](https://github.com/babel/babylon/pull/163)) (Jordan Gensler)
+
+#### Dynamic Import
+
+- Proposal Repo: https://github.com/domenic/proposal-dynamic-import
+- Championed by [@domenic](https://github.com/domenic)
+- stage-2
+- [sept-28 tc39 notes](https://github.com/rwaldron/tc39-notes/blob/master/es7/2016-09/sept-28.md#113a-import)
+
+> This repository contains a proposal for adding a "function-like" import() module loading syntactic form to JavaScript
+
+```js
+import(`./section-modules/${link.dataset.entryModule}.js`)
+.then(module => {
+  module.loadPageInto(main);
+})
+```
+
+Add EmptyTypeAnnotation ([#171](https://github.com/babel/babylon/pull/171)) (Sam Goldman)
+
+#### EmptyTypeAnnotation
+
+Just wasn't covered before.
+
+```js
+type T = empty;
+```
+
+### :bug: Bug Fix
+
+Fix crash when exporting with destructuring and sparse array ([#170](https://github.com/babel/babylon/pull/170)) (Jeroen Engels)
+
+```js
+// was failing due to sparse array
+export const { foo: [ ,, qux7 ] } = bar;
+```
+
+Allow keyword in Flow object declaration property names with type parameters ([#146](https://github.com/babel/babylon/pull/146)) (Dan Harper)
+
+```js
+declare class X {
+  foobar<T>(): void;
+  static foobar<T>(): void;
+}
+```
+
+Allow keyword in object/class property names with Flow type parameters ([#145](https://github.com/babel/babylon/pull/145)) (Dan Harper)
+
+```js
+class Foo {
+  delete<T>(item: T): T {
+    return item;
+  }
+}
+```
+
+Allow typeAnnotations for yield expressions ([#174](https://github.com/babel/babylon/pull/174))) (Daniel Tschinder)
+
+```js
+function *foo() {
+  const x = (yield 5: any);
+}
+```
+
+### :nail_care: Polish
+
+Annotate more errors with expected token ([#172](https://github.com/babel/babylon/pull/172))) (Moti Zilberman)
+
+```js
+// Unexpected token, expected ; (1:6)
+{ set 1 }
+```
+
+### :house: Internal
+
+Remove kcheck ([#173](https://github.com/babel/babylon/pull/173)))  (Daniel Tschinder)
+
+Also run flow, linting, babel tests on separate instances (add back node 0.10)
+
+## v6.11.6 (2016-10-12)
+
+### :bug: Bug Fix/Regression
+
+Fix crash when exporting with destructuring and sparse array ([#170](https://github.com/babel/babylon/pull/170)) (Jeroen Engels)
+
+```js
+// was failing with `Cannot read property 'type' of null` because of null identifiers
+export const { foo: [ ,, qux7 ] } = bar;
+```
+
+## v6.11.5 (2016-10-12)
+
+### :eyeglasses: Spec Compliance
+
+Fix: Check for duplicate named exports in exported destructuring assignments ([#144](https://github.com/babel/babylon/pull/144)) (Kai Cataldo)
+
+```js
+// `foo` has already been exported. Exported identifiers must be unique. (2:20)
+export function foo() {};
+export const { a: [{foo}] } = bar;
+```
+
+Fix: Check for duplicate named exports in exported rest elements/properties ([#164](https://github.com/babel/babylon/pull/164)) (Kai Cataldo)
+
+```js
+// `foo` has already been exported. Exported identifiers must be unique. (2:22)
+export const foo = 1;
+export const [bar, ...foo] = baz;
+```
+
+### :bug: Bug Fix
+
+Fix: Allow identifier `async` for default param in arrow expression ([#165](https://github.com/babel/babylon/pull/165)) (Kai Cataldo)
+
+```js
+// this is ok now
+const test = ({async = true}) => {};
+```
+
+### :nail_care: Polish
+
+Babylon will now print out the token it's expecting if there's a `SyntaxError` ([#150](https://github.com/babel/babylon/pull/150)) (Daniel Tschinder)
+
+```bash
+# So in the case of a missing ending curly (`}`)
+Module build failed: SyntaxError: Unexpected token, expected } (30:0)
+  28 |   }
+  29 |
+> 30 |
+     | ^
+```
+
+## v6.11.4 (2016-10-03)
+
+Temporary rollback for erroring on trailing comma with spread (#154) (Henry Zhu)
+
+## v6.11.3 (2016-10-01)
+
+### :eyeglasses: Spec Compliance
+
+Add static errors for object rest (#149) ([@danez](https://github.com/danez))
+
+> https://github.com/sebmarkbage/ecmascript-rest-spread
+
+Object rest copies the *rest* of properties from the right hand side `obj` starting from the left to right.
+
+```js
+let { x, y, ...z } =  { x: 1, y: 2, z: 3 };
+// x = 1
+// y = 2
+// z = { z: 3 }
+```
+
+#### New Syntax Errors:
+
+**SyntaxError**: The rest element has to be the last element when destructuring (1:10)
+```bash
+> 1 | let { ...x, y, z } = { x: 1, y: 2, z: 3};
+    |           ^
+# Previous behavior:
+# x = { x: 1, y: 2, z: 3 }
+# y = 2
+# z = 3
+```
+
+Before, this was just a more verbose way of shallow copying `obj` since it doesn't actually do what you think.
+
+**SyntaxError**: Cannot have multiple rest elements when destructuring (1:13)
+
+```bash
+> 1 | let { x, ...y, ...z } = { x: 1, y: 2, z: 3};
+    |              ^
+# Previous behavior:
+# x = 1
+# y = { y: 2, z: 3 }
+# z = { y: 2, z: 3 }
+```
+
+Before y and z would just be the same value anyway so there is no reason to need to have both.
+
+**SyntaxError**: A trailing comma is not permitted after the rest element (1:16)
+
+```js
+let { x, y, ...z, } = obj;
+```
+
+The rationale for this is that the use case for trailing comma is that you can add something at the end without affecting the line above. Since a RestProperty always has to be the last property it doesn't make sense.
+
+---
+
+get / set are valid property names in default assignment (#142) ([@jezell](https://github.com/jezell))
+
+```js
+// valid
+function something({ set = null, get = null }) {}
+```
+
+## v6.11.2 (2016-09-23)
+
+### Bug Fix
+
+- [#139](https://github.com/babel/babylon/issues/139) Don't do the duplicate check if not an identifier (#140) @hzoo
+
+```js
+// regression with duplicate export check
+SyntaxError: ./typography.js: `undefined` has already been exported. Exported identifiers must be unique. (22:13)
+  20 |
+  21 | export const { rhythm } = typography;
+> 22 | export const { TypographyStyle } = typography
+```
+
+Bail out for now, and make a change to account for destructuring in the next release.
+
+## 6.11.1 (2016-09-22)
+
+### Bug Fix
+- [#137](https://github.com/babel/babylon/pull/137) - Fix a regression with duplicate exports - it was erroring on all keys in `Object.prototype`. @danez
+
+```javascript
+export toString from './toString';
+```
+
+```bash
+`toString` has already been exported. Exported identifiers must be unique. (1:7)
+> 1 | export toString from './toString';
+    |        ^
+  2 |
+```
+
+## 6.11.0 (2016-09-22)
+
+### Spec Compliance (will break CI)
+
+- Disallow duplicate named exports ([#107](https://github.com/babel/babylon/pull/107)) @kaicataldo
+
+```js
+// Only one default export allowed per module. (2:9)
+export default function() {};
+export { foo as default };
+
+// Only one default export allowed per module. (2:0)
+export default {};
+export default function() {};
+
+// `Foo` has already been exported. Exported identifiers must be unique. (2:0)
+export { Foo };
+export class Foo {};
+```
+
+### New Feature (Syntax)
+
+- Add support for computed class property names ([#121](https://github.com/babel/babylon/pull/121)) @motiz88
+
+```js
+// AST
+interface ClassProperty <: Node {
+  type: "ClassProperty";
+  key: Identifier;
+  value: Expression;
+  computed: boolean; // added
+}
+```
+
+```js
+// with "plugins": ["classProperties"]
+class Foo {
+  [x]
+  ['y']
+}
+
+class Bar {
+  [p]
+  [m] () {}
+}
+ ```
+
+### Bug Fix
+
+- Fix `static` property falling through in the declare class Flow AST ([#135](https://github.com/babel/babylon/pull/135)) @danharper
+
+```js
+declare class X {
+    a: number;
+    static b: number; // static
+    c: number; // this was being marked as static in the AST as well
+}
+```
+
+### Polish
+
+- Rephrase "assigning/binding to rvalue" errors to include context ([#119](https://github.com/babel/babylon/pull/119)) @motiz88
+
+```js
+// Used to error with:
+// SyntaxError: Assigning to rvalue (1:0)
+
+// Now:
+// Invalid left-hand side in assignment expression (1:0)
+3 = 4
+
+// Invalid left-hand side in for-in statement (1:5)
+for (+i in {});
+```
+
+### Internal
+
+- Fix call to `this.parseMaybeAssign` with correct arguments ([#133](https://github.com/babel/babylon/pull/133)) @danez
+- Add semver note to changelog ([#131](https://github.com/babel/babylon/pull/131)) @hzoo
+
+## 6.10.0 (2016-09-19)
+
+> We plan to include some spec compliance bugs in patch versions. An example was the multiple default exports issue.
+
+### Spec Compliance
+
+* Implement ES2016 check for simple parameter list in strict mode ([#106](https://github.com/babel/babylon/pull/106)) (Timothy Gu)
+
+> It is a Syntax Error if ContainsUseStrict of FunctionBody is true and IsSimpleParameterList of FormalParameters is false. https://tc39.github.io/ecma262/2016/#sec-function-definitions-static-semantics-early-errors
+
+More Context: [tc39-notes](https://github.com/rwaldron/tc39-notes/blob/master/es7/2015-07/july-29.md#611-the-scope-of-use-strict-with-respect-to-destructuring-in-parameter-lists)
+
+For example:
+
+```js
+// this errors because it uses destructuring and default parameters
+// in a function with a "use strict" directive
+function a([ option1, option2 ] = []) {
+  "use strict";
+}
+ ```
+
+The solution would be to use a top level "use strict" or to remove the destructuring or default parameters when using a function + "use strict" or to.
+
+### New Feature
+
+* Exact object type annotations for Flow plugin ([#104](https://github.com/babel/babylon/pull/104)) (Basil Hosmer)
+
+Added to flow in https://github.com/facebook/flow/commit/c710c40aa2a115435098d6c0dfeaadb023cd39b8
+
+Looks like:
+
+```js
+var a : {| x: number, y: string |} = { x: 0, y: 'foo' };
+```
+
+### Bug Fixes
+
+* Include `typeParameter` location in `ArrowFunctionExpression` ([#126](https://github.com/babel/babylon/pull/126)) (Daniel Tschinder)
+* Error on invalid flow type annotation with default assignment ([#122](https://github.com/babel/babylon/pull/122)) (Dan Harper)
+* Fix Flow return types on arrow functions ([#124](https://github.com/babel/babylon/pull/124)) (Dan Harper)
+
+### Misc
+
+* Add tests for export extensions ([#127](https://github.com/babel/babylon/pull/127)) (Daniel Tschinder)
+* Fix Contributing guidelines [skip ci] (Daniel Tschinder)
+
+## 6.9.2 (2016-09-09)
+
+The only change is to remove the `babel-runtime` dependency by compiling with Babel's ES2015 loose mode. So using babylon standalone should be smaller.
+
+## 6.9.1 (2016-08-23)
+
+This release contains mainly small bugfixes but also updates babylons default mode to es2017. The features for `exponentiationOperator`, `asyncFunctions` and `trailingFunctionCommas` which previously needed to be activated via plugin are now enabled by default and the plugins are now no-ops.
+
+### Bug Fixes
+
+- Fix issues with default object params in async functions ([#96](https://github.com/babel/babylon/pull/96)) @danez
+- Fix issues with flow-types and async function ([#95](https://github.com/babel/babylon/pull/95)) @danez
+- Fix arrow functions with destructuring, types & default value ([#94](https://github.com/babel/babylon/pull/94)) @danharper
+- Fix declare class with qualified type identifier ([#97](https://github.com/babel/babylon/pull/97)) @danez
+- Remove exponentiationOperator, asyncFunctions, trailingFunctionCommas plugins and enable them by default ([#98](https://github.com/babel/babylon/pull/98)) @danez
+
+## 6.9.0 (2016-08-16)
+
+### New syntax support
+
+- Add JSX spread children ([#42](https://github.com/babel/babylon/pull/42)) @calebmer
+
+(Be aware that React is not going to support this syntax)
+
+```js
+<div>
+  {...todos.map(todo => <Todo key={todo.id} todo={todo}/>)}
+</div>
+```
+
+- Add support for declare module.exports ([#72](https://github.com/babel/babylon/pull/72)) @danez
+
+```js
+declare module "foo" {
+  declare module.exports: {}
+}
+```
+
+### New Features
+
+- If supplied, attach filename property to comment node loc. ([#80](https://github.com/babel/babylon/pull/80)) @divmain
+- Add identifier name to node loc field ([#90](https://github.com/babel/babylon/pull/90)) @kittens
+
+### Bug Fixes
+
+- Fix exponential operator to behave according to spec ([#75](https://github.com/babel/babylon/pull/75)) @danez
+- Fix lookahead to not add comments to arrays which are not cloned ([#76](https://github.com/babel/babylon/pull/76)) @danez
+- Fix accidental fall-through in Flow type parsing. ([#82](https://github.com/babel/babylon/pull/82)) @xiemaisi
+- Only allow declares inside declare module ([#73](https://github.com/babel/babylon/pull/73)) @danez
+- Small fix for parsing type parameter declarations ([#83](https://github.com/babel/babylon/pull/83)) @gabelevi
+- Fix arrow param locations with flow types ([#57](https://github.com/babel/babylon/pull/57)) @danez
+- Fixes SyntaxError position with flow optional type ([#65](https://github.com/babel/babylon/pull/65)) @danez
+
+### Internal
+
+- Add codecoverage to tests @danez
+- Fix tests to not save expected output if we expect the test to fail @danez
+- Make a shallow clone of babel for testing @danez
+- chore(package): update cross-env to version 2.0.0 ([#77](https://github.com/babel/babylon/pull/77)) @greenkeeperio-bot
+- chore(package): update ava to version 0.16.0 ([#86](https://github.com/babel/babylon/pull/86)) @greenkeeperio-bot
+- chore(package): update babel-plugin-istanbul to version 2.0.0 ([#89](https://github.com/babel/babylon/pull/89)) @greenkeeperio-bot
+- chore(package): update nyc to version 8.0.0 ([#88](https://github.com/babel/babylon/pull/88)) @greenkeeperio-bot
+
+## 6.8.4 (2016-07-06)
+
+### Bug Fixes
+
+- Fix the location of params, when flow and default value used ([#68](https://github.com/babel/babylon/pull/68)) @danez
+
+## 6.8.3 (2016-07-02)
+
+### Bug Fixes
+
+- Fix performance regression introduced in 6.8.2 with conditionals ([#63](https://github.com/babel/babylon/pull/63)) @danez
+
+## 6.8.2 (2016-06-24)
+
+### Bug Fixes
+
+- Fix parse error with yielding jsx elements in generators `function* it() { yield <a></a>; }` ([#31](https://github.com/babel/babylon/pull/31)) @eldereal
+- When cloning nodes do not clone its comments ([#24](https://github.com/babel/babylon/pull/24)) @danez
+- Fix parse errors when using arrow functions with an spread element and return type `(...props): void => {}` ([#10](https://github.com/babel/babylon/pull/10)) @danez
+- Fix leading comments added from previous node ([#23](https://github.com/babel/babylon/pull/23)) @danez
+- Fix parse errors with flow's optional arguments `(arg?) => {}` ([#19](https://github.com/babel/babylon/pull/19)) @danez
+- Support negative numeric type literals @kittens
+- Remove line terminator restriction after await keyword @kittens
+- Remove grouped type arrow restriction as it seems flow no longer has it @kittens
+- Fix parse error with generic methods that have the name `get` or `set` `class foo { get() {} }` ([#55](https://github.com/babel/babylon/pull/55)) @vkurchatkin
+- Fix parse error with arrow functions that have flow type parameter declarations `<T>(x: T): T => x;` ([#54](https://github.com/babel/babylon/pull/54)) @gabelevi
+
+### Documentation
+
+- Document AST differences from ESTree ([#41](https://github.com/babel/babylon/pull/41)) @nene
+- Move ast spec from babel/babel ([#46](https://github.com/babel/babylon/pull/46)) @hzoo
+
+### Internal
+
+- Enable skipped tests ([#16](https://github.com/babel/babylon/pull/16)) @danez
+- Add script to test latest version of babylon with babel ([#21](https://github.com/babel/babylon/pull/21)) @danez
+- Upgrade test runner ava @kittens
+- Add missing generate-identifier-regex script @kittens
+- Rename parser context types @kittens
+- Add node v6 to travis testing @hzoo
+- Update to Unicode v9 ([#45](https://github.com/babel/babylon/pull/45)) @mathiasbynens
+
+## 6.8.1 (2016-06-06)
+
+### New Feature
+
+- Parse type parameter declarations with defaults like `type Foo<T = string> = T`
+
+### Bug Fixes
+- Type parameter declarations need 1 or more type parameters.
+- The existential type `*` is not a valid type parameter.
+- The existential type `*` is a primary type
+
+### Spec Compliance
+- The param list for type parameter declarations now consists of `TypeParameter` nodes
+- New `TypeParameter` AST Node (replaces using the `Identifier` node before)
+
+```
+interface TypeParameter <: Node {
+  bound: TypeAnnotation;
+  default: TypeAnnotation;
+  name: string;
+  variance: "plus" | "minus";
+}
+```
+
+## 6.8.0 (2016-05-02)
+
+#### New Feature
+
+##### Parse Method Parameter Decorators ([#12](https://github.com/babel/babylon/pull/12))
+
+> [Method Parameter Decorators](https://goo.gl/8MmCMG) is now a TC39 [stage 0 proposal](https://github.com/tc39/ecma262/blob/master/stage0.md).
+
+Examples:
+
+```js
+class Foo {
+  constructor(@foo() x, @bar({ a: 123 }) @baz() y) {}
+}
+
+export default function func(@foo() x, @bar({ a: 123 }) @baz() y) {}
+
+var obj = {
+  method(@foo() x, @bar({ a: 123 }) @baz() y) {}
+};
+```
+
+##### Parse for-await statements (w/ `asyncGenerators` plugin) ([#17](https://github.com/babel/babylon/pull/17))
+
+There is also a new node type, `ForAwaitStatement`.
+
+> [Async generators and for-await](https://github.com/tc39/proposal-async-iteration) are now a [stage 2 proposal](https://github.com/tc39/ecma262#current-proposals).
+
+Example:
+
+```js
+async function f() {
+  for await (let x of y);
+}
+```

+ 19 - 0
node_modules/@babel/parser/LICENSE

@@ -0,0 +1,19 @@
+Copyright (C) 2012-2014 by various contributors (see AUTHORS)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 19 - 0
node_modules/@babel/parser/README.md

@@ -0,0 +1,19 @@
+# @babel/parser
+
+> A JavaScript parser
+
+See our website [@babel/parser](https://babeljs.io/docs/babel-parser) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20parser%22+is%3Aopen) associated with this package.
+
+## Install
+
+Using npm:
+
+```sh
+npm install --save-dev @babel/parser
+```
+
+or using yarn:
+
+```sh
+yarn add @babel/parser --dev
+```

+ 15 - 0
node_modules/@babel/parser/bin/babel-parser.js

@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+/* eslint-disable no-var, unicorn/prefer-node-protocol */
+
+var parser = require("..");
+var fs = require("fs");
+
+var filename = process.argv[2];
+if (!filename) {
+  console.error("no filename specified");
+} else {
+  var file = fs.readFileSync(filename, "utf8");
+  var ast = parser.parse(file);
+
+  console.log(JSON.stringify(ast, null, "  "));
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1442 - 0
node_modules/@babel/parser/lib/index.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/parser/lib/index.js.map


+ 50 - 0
node_modules/@babel/parser/package.json

@@ -0,0 +1,50 @@
+{
+  "name": "@babel/parser",
+  "version": "7.29.8",
+  "description": "A JavaScript parser",
+  "author": "The Babel Team (https://babel.dev/team)",
+  "homepage": "https://babel.dev/docs/en/next/babel-parser",
+  "bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen",
+  "license": "MIT",
+  "publishConfig": {
+    "access": "public"
+  },
+  "keywords": [
+    "babel",
+    "javascript",
+    "parser",
+    "tc39",
+    "ecmascript",
+    "@babel/parser"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/babel/babel.git",
+    "directory": "packages/babel-parser"
+  },
+  "main": "./lib/index.js",
+  "types": "./typings/babel-parser.d.ts",
+  "files": [
+    "bin",
+    "lib",
+    "typings/babel-parser.d.ts",
+    "index.cjs"
+  ],
+  "engines": {
+    "node": ">=6.0.0"
+  },
+  "# dependencies": "This package doesn't actually have runtime dependencies. @babel/types is only needed for type definitions.",
+  "dependencies": {
+    "@babel/types": "^7.29.8"
+  },
+  "devDependencies": {
+    "@babel/code-frame": "^7.29.7",
+    "@babel/helper-check-duplicate-nodes": "^7.29.7",
+    "@babel/helper-fixtures": "^7.29.7",
+    "@babel/helper-string-parser": "^7.29.7",
+    "@babel/helper-validator-identifier": "^7.29.7",
+    "charcodes": "^0.2.0"
+  },
+  "bin": "./bin/babel-parser.js",
+  "type": "commonjs"
+}

+ 262 - 0
node_modules/@babel/parser/typings/babel-parser.d.ts

@@ -0,0 +1,262 @@
+// This file is auto-generated! Do not modify it directly.
+// Run `yarn gulp bundle-dts` to re-generate it.
+/* eslint-disable @typescript-eslint/consistent-type-imports, @typescript-eslint/no-redundant-type-constituents */
+import { File, Expression } from '@babel/types';
+
+declare class Position {
+    line: number;
+    column: number;
+    index: number;
+    constructor(line: number, col: number, index: number);
+}
+
+type SyntaxPlugin = "flow" | "typescript" | "jsx" | "functionBind" | "pipelineOperator" | "placeholders";
+type ParseErrorCode = "BABEL_PARSER_SYNTAX_ERROR" | "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED";
+interface ParseErrorSpecification<ErrorDetails> {
+    code: ParseErrorCode;
+    reasonCode: string;
+    syntaxPlugin?: SyntaxPlugin;
+    missingPlugin?: string | string[];
+    loc: Position;
+    details: ErrorDetails;
+    pos: number;
+}
+type ParseError$1<ErrorDetails> = SyntaxError & ParseErrorSpecification<ErrorDetails>;
+
+type BABEL_8_BREAKING = false;
+type IF_BABEL_7<V> = false extends BABEL_8_BREAKING ? V : never;
+
+type Plugin$1 =
+  | "asyncDoExpressions"
+  | IF_BABEL_7<"asyncGenerators">
+  | IF_BABEL_7<"bigInt">
+  | IF_BABEL_7<"classPrivateMethods">
+  | IF_BABEL_7<"classPrivateProperties">
+  | IF_BABEL_7<"classProperties">
+  | IF_BABEL_7<"classStaticBlock">
+  | IF_BABEL_7<"decimal">
+  | "decorators-legacy"
+  | "deferredImportEvaluation"
+  | "decoratorAutoAccessors"
+  | "destructuringPrivate"
+  | IF_BABEL_7<"deprecatedImportAssert">
+  | "doExpressions"
+  | IF_BABEL_7<"dynamicImport">
+  | IF_BABEL_7<"explicitResourceManagement">
+  | "exportDefaultFrom"
+  | IF_BABEL_7<"exportNamespaceFrom">
+  | "flow"
+  | "flowComments"
+  | "functionBind"
+  | "functionSent"
+  | "importMeta"
+  | "jsx"
+  | IF_BABEL_7<"jsonStrings">
+  | IF_BABEL_7<"logicalAssignment">
+  | IF_BABEL_7<"importAssertions">
+  | IF_BABEL_7<"importReflection">
+  | "moduleBlocks"
+  | IF_BABEL_7<"moduleStringNames">
+  | IF_BABEL_7<"nullishCoalescingOperator">
+  | IF_BABEL_7<"numericSeparator">
+  | IF_BABEL_7<"objectRestSpread">
+  | IF_BABEL_7<"optionalCatchBinding">
+  | IF_BABEL_7<"optionalChaining">
+  | "partialApplication"
+  | "placeholders"
+  | IF_BABEL_7<"privateIn">
+  | IF_BABEL_7<"regexpUnicodeSets">
+  | "sourcePhaseImports"
+  | "throwExpressions"
+  | IF_BABEL_7<"topLevelAwait">
+  | "v8intrinsic"
+  | ParserPluginWithOptions[0];
+
+type ParserPluginWithOptions =
+  | ["decorators", DecoratorsPluginOptions]
+  | ["discardBinding", { syntaxType: "void" }]
+  | ["estree", { classFeatures?: boolean }]
+  | IF_BABEL_7<["importAttributes", { deprecatedAssertSyntax: boolean }]>
+  | IF_BABEL_7<["moduleAttributes", { version: "may-2020" }]>
+  | ["optionalChainingAssign", { version: "2023-07" }]
+  | ["pipelineOperator", PipelineOperatorPluginOptions]
+  | ["recordAndTuple", RecordAndTuplePluginOptions]
+  | ["flow", FlowPluginOptions]
+  | ["typescript", TypeScriptPluginOptions];
+
+type PluginConfig = Plugin$1 | ParserPluginWithOptions;
+
+interface DecoratorsPluginOptions {
+  decoratorsBeforeExport?: boolean;
+  allowCallParenthesized?: boolean;
+}
+
+interface PipelineOperatorPluginOptions {
+  proposal: BABEL_8_BREAKING extends false
+    ? "minimal" | "fsharp" | "hack" | "smart"
+    : "fsharp" | "hack";
+  topicToken?: "%" | "#" | "@@" | "^^" | "^";
+}
+
+interface RecordAndTuplePluginOptions {
+  syntaxType: "bar" | "hash";
+}
+
+type FlowPluginOptions = BABEL_8_BREAKING extends true
+  ? {
+      all?: boolean;
+      enums?: boolean;
+    }
+  : {
+      all?: boolean;
+    };
+
+interface TypeScriptPluginOptions {
+  dts?: boolean;
+  disallowAmbiguousJSXLike?: boolean;
+}
+
+type Plugin = PluginConfig;
+
+type SourceType = "script" | "commonjs" | "module" | "unambiguous";
+interface Options {
+    /**
+     * By default, import and export declarations can only appear at a program's top level.
+     * Setting this option to true allows them anywhere where a statement is allowed.
+     */
+    allowImportExportEverywhere?: boolean;
+    /**
+     * By default, await use is not allowed outside of an async function.
+     * Set this to true to accept such code.
+     */
+    allowAwaitOutsideFunction?: boolean;
+    /**
+     * By default, a return statement at the top level raises an error.
+     * Set this to true to accept such code.
+     */
+    allowReturnOutsideFunction?: boolean;
+    /**
+     * By default, new.target use is not allowed outside of a function or class.
+     * Set this to true to accept such code.
+     */
+    allowNewTargetOutsideFunction?: boolean;
+    /**
+     * By default, super calls are not allowed outside of a method.
+     * Set this to true to accept such code.
+     */
+    allowSuperOutsideMethod?: boolean;
+    /**
+     * By default, exported identifiers must refer to a declared variable.
+     * Set this to true to allow export statements to reference undeclared variables.
+     */
+    allowUndeclaredExports?: boolean;
+    /**
+     * By default, yield use is not allowed outside of a generator function.
+     * Set this to true to accept such code.
+     */
+    allowYieldOutsideFunction?: boolean;
+    /**
+     * By default, Babel parser JavaScript code according to Annex B syntax.
+     * Set this to `false` to disable such behavior.
+     */
+    annexB?: boolean;
+    /**
+     * By default, Babel attaches comments to adjacent AST nodes.
+     * When this option is set to false, comments are not attached.
+     * It can provide up to 30% performance improvement when the input code has many comments.
+     * @babel/eslint-parser will set it for you.
+     * It is not recommended to use attachComment: false with Babel transform,
+     * as doing so removes all the comments in output code, and renders annotations such as
+     * /* istanbul ignore next *\/ nonfunctional.
+     */
+    attachComment?: boolean;
+    /**
+     * By default, Babel always throws an error when it finds some invalid code.
+     * When this option is set to true, it will store the parsing error and
+     * try to continue parsing the invalid input file.
+     */
+    errorRecovery?: boolean;
+    /**
+     * Indicate the mode the code should be parsed in.
+     * Can be one of "script", "commonjs", "module", or "unambiguous". Defaults to "script".
+     * "unambiguous" will make @babel/parser attempt to guess, based on the presence
+     * of ES6 import or export statements.
+     * Files with ES6 imports and exports are considered "module" and are otherwise "script".
+     *
+     * Use "commonjs" to parse code that is intended to be run in a CommonJS environment such as Node.js.
+     */
+    sourceType?: SourceType;
+    /**
+     * Correlate output AST nodes with their source filename.
+     * Useful when generating code and source maps from the ASTs of multiple input files.
+     */
+    sourceFilename?: string;
+    /**
+     * By default, all source indexes start from 0.
+     * You can provide a start index to alternatively start with.
+     * Useful for integration with other source tools.
+     */
+    startIndex?: number;
+    /**
+     * By default, the first line of code parsed is treated as line 1.
+     * You can provide a line number to alternatively start with.
+     * Useful for integration with other source tools.
+     */
+    startLine?: number;
+    /**
+     * By default, the parsed code is treated as if it starts from line 1, column 0.
+     * You can provide a column number to alternatively start with.
+     * Useful for integration with other source tools.
+     */
+    startColumn?: number;
+    /**
+     * Array containing the plugins that you want to enable.
+     */
+    plugins?: Plugin[];
+    /**
+     * Should the parser work in strict mode.
+     * Defaults to true if sourceType === 'module'. Otherwise, false.
+     */
+    strictMode?: boolean;
+    /**
+     * Adds a ranges property to each node: [node.start, node.end]
+     */
+    ranges?: boolean;
+    /**
+     * Adds all parsed tokens to a tokens property on the File node.
+     */
+    tokens?: boolean;
+    /**
+     * By default, the parser adds information about parentheses by setting
+     * `extra.parenthesized` to `true` as needed.
+     * When this option is `true` the parser creates `ParenthesizedExpression`
+     * AST nodes instead of using the `extra` property.
+     */
+    createParenthesizedExpressions?: boolean;
+    /**
+     * The default is false in Babel 7 and true in Babel 8
+     * Set this to true to parse it as an `ImportExpression` node.
+     * Otherwise `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`.
+     */
+    createImportExpressions?: boolean;
+}
+
+type ParserOptions = Partial<Options>;
+type ParseError = ParseError$1<object>;
+type ParseResult<Result extends File | Expression = File> = Result & {
+    comments: File["comments"];
+    errors: null | ParseError[];
+    tokens?: File["tokens"];
+};
+/**
+ * Parse the provided code as an entire ECMAScript program.
+ */
+declare function parse(input: string, options?: ParserOptions): ParseResult<File>;
+declare function parseExpression(input: string, options?: ParserOptions): ParseResult<Expression>;
+
+declare const tokTypes: {
+  // todo(flow->ts) real token type
+  [name: string]: any;
+};
+
+export { DecoratorsPluginOptions, FlowPluginOptions, ParseError, ParseResult, ParserOptions, PluginConfig as ParserPlugin, ParserPluginWithOptions, PipelineOperatorPluginOptions, RecordAndTuplePluginOptions, TypeScriptPluginOptions, parse, parseExpression, tokTypes };

+ 22 - 0
node_modules/@babel/types/LICENSE

@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2014-present Sebastian McKenzie and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 19 - 0
node_modules/@babel/types/README.md

@@ -0,0 +1,19 @@
+# @babel/types
+
+> Babel Types is a Lodash-esque utility library for AST nodes
+
+See our website [@babel/types](https://babeljs.io/docs/babel-types) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20types%22+is%3Aopen) associated with this package.
+
+## Install
+
+Using npm:
+
+```sh
+npm install --save-dev @babel/types
+```
+
+or using yarn:
+
+```sh
+yarn add @babel/types --dev
+```

+ 16 - 0
node_modules/@babel/types/lib/asserts/assertNode.js

@@ -0,0 +1,16 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = assertNode;
+var _isNode = require("../validators/isNode.js");
+function assertNode(node) {
+  if (!(0, _isNode.default)(node)) {
+    var _node$type;
+    const type = (_node$type = node == null ? void 0 : node.type) != null ? _node$type : JSON.stringify(node);
+    throw new TypeError(`Not a valid node of type "${type}"`);
+  }
+}
+
+//# sourceMappingURL=assertNode.js.map

+ 1 - 0
node_modules/@babel/types/lib/asserts/assertNode.js.map

@@ -0,0 +1 @@
+{"version":3,"names":["_isNode","require","assertNode","node","isNode","_node$type","type","JSON","stringify","TypeError"],"sources":["../../src/asserts/assertNode.ts"],"sourcesContent":["import isNode from \"../validators/isNode.ts\";\nimport type * as t from \"../index.ts\";\n\nexport default function assertNode(node?: any): asserts node is t.Node {\n  if (!isNode(node)) {\n    const type = node?.type ?? JSON.stringify(node);\n    throw new TypeError(`Not a valid node of type \"${type}\"`);\n  }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAGe,SAASC,UAAUA,CAACC,IAAU,EAA0B;EACrE,IAAI,CAAC,IAAAC,eAAM,EAACD,IAAI,CAAC,EAAE;IAAA,IAAAE,UAAA;IACjB,MAAMC,IAAI,IAAAD,UAAA,GAAGF,IAAI,oBAAJA,IAAI,CAAEG,IAAI,YAAAD,UAAA,GAAIE,IAAI,CAACC,SAAS,CAACL,IAAI,CAAC;IAC/C,MAAM,IAAIM,SAAS,CAAC,6BAA6BH,IAAI,GAAG,CAAC;EAC3D;AACF","ignoreList":[]}

+ 1251 - 0
node_modules/@babel/types/lib/asserts/generated/index.js

@@ -0,0 +1,1251 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.assertAccessor = assertAccessor;
+exports.assertAnyTypeAnnotation = assertAnyTypeAnnotation;
+exports.assertArgumentPlaceholder = assertArgumentPlaceholder;
+exports.assertArrayExpression = assertArrayExpression;
+exports.assertArrayPattern = assertArrayPattern;
+exports.assertArrayTypeAnnotation = assertArrayTypeAnnotation;
+exports.assertArrowFunctionExpression = assertArrowFunctionExpression;
+exports.assertAssignmentExpression = assertAssignmentExpression;
+exports.assertAssignmentPattern = assertAssignmentPattern;
+exports.assertAwaitExpression = assertAwaitExpression;
+exports.assertBigIntLiteral = assertBigIntLiteral;
+exports.assertBinary = assertBinary;
+exports.assertBinaryExpression = assertBinaryExpression;
+exports.assertBindExpression = assertBindExpression;
+exports.assertBlock = assertBlock;
+exports.assertBlockParent = assertBlockParent;
+exports.assertBlockStatement = assertBlockStatement;
+exports.assertBooleanLiteral = assertBooleanLiteral;
+exports.assertBooleanLiteralTypeAnnotation = assertBooleanLiteralTypeAnnotation;
+exports.assertBooleanTypeAnnotation = assertBooleanTypeAnnotation;
+exports.assertBreakStatement = assertBreakStatement;
+exports.assertCallExpression = assertCallExpression;
+exports.assertCatchClause = assertCatchClause;
+exports.assertClass = assertClass;
+exports.assertClassAccessorProperty = assertClassAccessorProperty;
+exports.assertClassBody = assertClassBody;
+exports.assertClassDeclaration = assertClassDeclaration;
+exports.assertClassExpression = assertClassExpression;
+exports.assertClassImplements = assertClassImplements;
+exports.assertClassMethod = assertClassMethod;
+exports.assertClassPrivateMethod = assertClassPrivateMethod;
+exports.assertClassPrivateProperty = assertClassPrivateProperty;
+exports.assertClassProperty = assertClassProperty;
+exports.assertCompletionStatement = assertCompletionStatement;
+exports.assertConditional = assertConditional;
+exports.assertConditionalExpression = assertConditionalExpression;
+exports.assertContinueStatement = assertContinueStatement;
+exports.assertDebuggerStatement = assertDebuggerStatement;
+exports.assertDecimalLiteral = assertDecimalLiteral;
+exports.assertDeclaration = assertDeclaration;
+exports.assertDeclareClass = assertDeclareClass;
+exports.assertDeclareExportAllDeclaration = assertDeclareExportAllDeclaration;
+exports.assertDeclareExportDeclaration = assertDeclareExportDeclaration;
+exports.assertDeclareFunction = assertDeclareFunction;
+exports.assertDeclareInterface = assertDeclareInterface;
+exports.assertDeclareModule = assertDeclareModule;
+exports.assertDeclareModuleExports = assertDeclareModuleExports;
+exports.assertDeclareOpaqueType = assertDeclareOpaqueType;
+exports.assertDeclareTypeAlias = assertDeclareTypeAlias;
+exports.assertDeclareVariable = assertDeclareVariable;
+exports.assertDeclaredPredicate = assertDeclaredPredicate;
+exports.assertDecorator = assertDecorator;
+exports.assertDirective = assertDirective;
+exports.assertDirectiveLiteral = assertDirectiveLiteral;
+exports.assertDoExpression = assertDoExpression;
+exports.assertDoWhileStatement = assertDoWhileStatement;
+exports.assertEmptyStatement = assertEmptyStatement;
+exports.assertEmptyTypeAnnotation = assertEmptyTypeAnnotation;
+exports.assertEnumBody = assertEnumBody;
+exports.assertEnumBooleanBody = assertEnumBooleanBody;
+exports.assertEnumBooleanMember = assertEnumBooleanMember;
+exports.assertEnumDeclaration = assertEnumDeclaration;
+exports.assertEnumDefaultedMember = assertEnumDefaultedMember;
+exports.assertEnumMember = assertEnumMember;
+exports.assertEnumNumberBody = assertEnumNumberBody;
+exports.assertEnumNumberMember = assertEnumNumberMember;
+exports.assertEnumStringBody = assertEnumStringBody;
+exports.assertEnumStringMember = assertEnumStringMember;
+exports.assertEnumSymbolBody = assertEnumSymbolBody;
+exports.assertExistsTypeAnnotation = assertExistsTypeAnnotation;
+exports.assertExportAllDeclaration = assertExportAllDeclaration;
+exports.assertExportDeclaration = assertExportDeclaration;
+exports.assertExportDefaultDeclaration = assertExportDefaultDeclaration;
+exports.assertExportDefaultSpecifier = assertExportDefaultSpecifier;
+exports.assertExportNamedDeclaration = assertExportNamedDeclaration;
+exports.assertExportNamespaceSpecifier = assertExportNamespaceSpecifier;
+exports.assertExportSpecifier = assertExportSpecifier;
+exports.assertExpression = assertExpression;
+exports.assertExpressionStatement = assertExpressionStatement;
+exports.assertExpressionWrapper = assertExpressionWrapper;
+exports.assertFile = assertFile;
+exports.assertFlow = assertFlow;
+exports.assertFlowBaseAnnotation = assertFlowBaseAnnotation;
+exports.assertFlowDeclaration = assertFlowDeclaration;
+exports.assertFlowPredicate = assertFlowPredicate;
+exports.assertFlowType = assertFlowType;
+exports.assertFor = assertFor;
+exports.assertForInStatement = assertForInStatement;
+exports.assertForOfStatement = assertForOfStatement;
+exports.assertForStatement = assertForStatement;
+exports.assertForXStatement = assertForXStatement;
+exports.assertFunction = assertFunction;
+exports.assertFunctionDeclaration = assertFunctionDeclaration;
+exports.assertFunctionExpression = assertFunctionExpression;
+exports.assertFunctionParameter = assertFunctionParameter;
+exports.assertFunctionParent = assertFunctionParent;
+exports.assertFunctionTypeAnnotation = assertFunctionTypeAnnotation;
+exports.assertFunctionTypeParam = assertFunctionTypeParam;
+exports.assertGenericTypeAnnotation = assertGenericTypeAnnotation;
+exports.assertIdentifier = assertIdentifier;
+exports.assertIfStatement = assertIfStatement;
+exports.assertImmutable = assertImmutable;
+exports.assertImport = assertImport;
+exports.assertImportAttribute = assertImportAttribute;
+exports.assertImportDeclaration = assertImportDeclaration;
+exports.assertImportDefaultSpecifier = assertImportDefaultSpecifier;
+exports.assertImportExpression = assertImportExpression;
+exports.assertImportNamespaceSpecifier = assertImportNamespaceSpecifier;
+exports.assertImportOrExportDeclaration = assertImportOrExportDeclaration;
+exports.assertImportSpecifier = assertImportSpecifier;
+exports.assertIndexedAccessType = assertIndexedAccessType;
+exports.assertInferredPredicate = assertInferredPredicate;
+exports.assertInterfaceDeclaration = assertInterfaceDeclaration;
+exports.assertInterfaceExtends = assertInterfaceExtends;
+exports.assertInterfaceTypeAnnotation = assertInterfaceTypeAnnotation;
+exports.assertInterpreterDirective = assertInterpreterDirective;
+exports.assertIntersectionTypeAnnotation = assertIntersectionTypeAnnotation;
+exports.assertJSX = assertJSX;
+exports.assertJSXAttribute = assertJSXAttribute;
+exports.assertJSXClosingElement = assertJSXClosingElement;
+exports.assertJSXClosingFragment = assertJSXClosingFragment;
+exports.assertJSXElement = assertJSXElement;
+exports.assertJSXEmptyExpression = assertJSXEmptyExpression;
+exports.assertJSXExpressionContainer = assertJSXExpressionContainer;
+exports.assertJSXFragment = assertJSXFragment;
+exports.assertJSXIdentifier = assertJSXIdentifier;
+exports.assertJSXMemberExpression = assertJSXMemberExpression;
+exports.assertJSXNamespacedName = assertJSXNamespacedName;
+exports.assertJSXOpeningElement = assertJSXOpeningElement;
+exports.assertJSXOpeningFragment = assertJSXOpeningFragment;
+exports.assertJSXSpreadAttribute = assertJSXSpreadAttribute;
+exports.assertJSXSpreadChild = assertJSXSpreadChild;
+exports.assertJSXText = assertJSXText;
+exports.assertLVal = assertLVal;
+exports.assertLabeledStatement = assertLabeledStatement;
+exports.assertLiteral = assertLiteral;
+exports.assertLogicalExpression = assertLogicalExpression;
+exports.assertLoop = assertLoop;
+exports.assertMemberExpression = assertMemberExpression;
+exports.assertMetaProperty = assertMetaProperty;
+exports.assertMethod = assertMethod;
+exports.assertMiscellaneous = assertMiscellaneous;
+exports.assertMixedTypeAnnotation = assertMixedTypeAnnotation;
+exports.assertModuleDeclaration = assertModuleDeclaration;
+exports.assertModuleExpression = assertModuleExpression;
+exports.assertModuleSpecifier = assertModuleSpecifier;
+exports.assertNewExpression = assertNewExpression;
+exports.assertNoop = assertNoop;
+exports.assertNullLiteral = assertNullLiteral;
+exports.assertNullLiteralTypeAnnotation = assertNullLiteralTypeAnnotation;
+exports.assertNullableTypeAnnotation = assertNullableTypeAnnotation;
+exports.assertNumberLiteral = assertNumberLiteral;
+exports.assertNumberLiteralTypeAnnotation = assertNumberLiteralTypeAnnotation;
+exports.assertNumberTypeAnnotation = assertNumberTypeAnnotation;
+exports.assertNumericLiteral = assertNumericLiteral;
+exports.assertObjectExpression = assertObjectExpression;
+exports.assertObjectMember = assertObjectMember;
+exports.assertObjectMethod = assertObjectMethod;
+exports.assertObjectPattern = assertObjectPattern;
+exports.assertObjectProperty = assertObjectProperty;
+exports.assertObjectTypeAnnotation = assertObjectTypeAnnotation;
+exports.assertObjectTypeCallProperty = assertObjectTypeCallProperty;
+exports.assertObjectTypeIndexer = assertObjectTypeIndexer;
+exports.assertObjectTypeInternalSlot = assertObjectTypeInternalSlot;
+exports.assertObjectTypeProperty = assertObjectTypeProperty;
+exports.assertObjectTypeSpreadProperty = assertObjectTypeSpreadProperty;
+exports.assertOpaqueType = assertOpaqueType;
+exports.assertOptionalCallExpression = assertOptionalCallExpression;
+exports.assertOptionalIndexedAccessType = assertOptionalIndexedAccessType;
+exports.assertOptionalMemberExpression = assertOptionalMemberExpression;
+exports.assertParenthesizedExpression = assertParenthesizedExpression;
+exports.assertPattern = assertPattern;
+exports.assertPatternLike = assertPatternLike;
+exports.assertPipelineBareFunction = assertPipelineBareFunction;
+exports.assertPipelinePrimaryTopicReference = assertPipelinePrimaryTopicReference;
+exports.assertPipelineTopicExpression = assertPipelineTopicExpression;
+exports.assertPlaceholder = assertPlaceholder;
+exports.assertPrivate = assertPrivate;
+exports.assertPrivateName = assertPrivateName;
+exports.assertProgram = assertProgram;
+exports.assertProperty = assertProperty;
+exports.assertPureish = assertPureish;
+exports.assertQualifiedTypeIdentifier = assertQualifiedTypeIdentifier;
+exports.assertRecordExpression = assertRecordExpression;
+exports.assertRegExpLiteral = assertRegExpLiteral;
+exports.assertRegexLiteral = assertRegexLiteral;
+exports.assertRestElement = assertRestElement;
+exports.assertRestProperty = assertRestProperty;
+exports.assertReturnStatement = assertReturnStatement;
+exports.assertScopable = assertScopable;
+exports.assertSequenceExpression = assertSequenceExpression;
+exports.assertSpreadElement = assertSpreadElement;
+exports.assertSpreadProperty = assertSpreadProperty;
+exports.assertStandardized = assertStandardized;
+exports.assertStatement = assertStatement;
+exports.assertStaticBlock = assertStaticBlock;
+exports.assertStringLiteral = assertStringLiteral;
+exports.assertStringLiteralTypeAnnotation = assertStringLiteralTypeAnnotation;
+exports.assertStringTypeAnnotation = assertStringTypeAnnotation;
+exports.assertSuper = assertSuper;
+exports.assertSwitchCase = assertSwitchCase;
+exports.assertSwitchStatement = assertSwitchStatement;
+exports.assertSymbolTypeAnnotation = assertSymbolTypeAnnotation;
+exports.assertTSAnyKeyword = assertTSAnyKeyword;
+exports.assertTSArrayType = assertTSArrayType;
+exports.assertTSAsExpression = assertTSAsExpression;
+exports.assertTSBaseType = assertTSBaseType;
+exports.assertTSBigIntKeyword = assertTSBigIntKeyword;
+exports.assertTSBooleanKeyword = assertTSBooleanKeyword;
+exports.assertTSCallSignatureDeclaration = assertTSCallSignatureDeclaration;
+exports.assertTSConditionalType = assertTSConditionalType;
+exports.assertTSConstructSignatureDeclaration = assertTSConstructSignatureDeclaration;
+exports.assertTSConstructorType = assertTSConstructorType;
+exports.assertTSDeclareFunction = assertTSDeclareFunction;
+exports.assertTSDeclareMethod = assertTSDeclareMethod;
+exports.assertTSEntityName = assertTSEntityName;
+exports.assertTSEnumBody = assertTSEnumBody;
+exports.assertTSEnumDeclaration = assertTSEnumDeclaration;
+exports.assertTSEnumMember = assertTSEnumMember;
+exports.assertTSExportAssignment = assertTSExportAssignment;
+exports.assertTSExpressionWithTypeArguments = assertTSExpressionWithTypeArguments;
+exports.assertTSExternalModuleReference = assertTSExternalModuleReference;
+exports.assertTSFunctionType = assertTSFunctionType;
+exports.assertTSImportEqualsDeclaration = assertTSImportEqualsDeclaration;
+exports.assertTSImportType = assertTSImportType;
+exports.assertTSIndexSignature = assertTSIndexSignature;
+exports.assertTSIndexedAccessType = assertTSIndexedAccessType;
+exports.assertTSInferType = assertTSInferType;
+exports.assertTSInstantiationExpression = assertTSInstantiationExpression;
+exports.assertTSInterfaceBody = assertTSInterfaceBody;
+exports.assertTSInterfaceDeclaration = assertTSInterfaceDeclaration;
+exports.assertTSIntersectionType = assertTSIntersectionType;
+exports.assertTSIntrinsicKeyword = assertTSIntrinsicKeyword;
+exports.assertTSLiteralType = assertTSLiteralType;
+exports.assertTSMappedType = assertTSMappedType;
+exports.assertTSMethodSignature = assertTSMethodSignature;
+exports.assertTSModuleBlock = assertTSModuleBlock;
+exports.assertTSModuleDeclaration = assertTSModuleDeclaration;
+exports.assertTSNamedTupleMember = assertTSNamedTupleMember;
+exports.assertTSNamespaceExportDeclaration = assertTSNamespaceExportDeclaration;
+exports.assertTSNeverKeyword = assertTSNeverKeyword;
+exports.assertTSNonNullExpression = assertTSNonNullExpression;
+exports.assertTSNullKeyword = assertTSNullKeyword;
+exports.assertTSNumberKeyword = assertTSNumberKeyword;
+exports.assertTSObjectKeyword = assertTSObjectKeyword;
+exports.assertTSOptionalType = assertTSOptionalType;
+exports.assertTSParameterProperty = assertTSParameterProperty;
+exports.assertTSParenthesizedType = assertTSParenthesizedType;
+exports.assertTSPropertySignature = assertTSPropertySignature;
+exports.assertTSQualifiedName = assertTSQualifiedName;
+exports.assertTSRestType = assertTSRestType;
+exports.assertTSSatisfiesExpression = assertTSSatisfiesExpression;
+exports.assertTSStringKeyword = assertTSStringKeyword;
+exports.assertTSSymbolKeyword = assertTSSymbolKeyword;
+exports.assertTSTemplateLiteralType = assertTSTemplateLiteralType;
+exports.assertTSThisType = assertTSThisType;
+exports.assertTSTupleType = assertTSTupleType;
+exports.assertTSType = assertTSType;
+exports.assertTSTypeAliasDeclaration = assertTSTypeAliasDeclaration;
+exports.assertTSTypeAnnotation = assertTSTypeAnnotation;
+exports.assertTSTypeAssertion = assertTSTypeAssertion;
+exports.assertTSTypeElement = assertTSTypeElement;
+exports.assertTSTypeLiteral = assertTSTypeLiteral;
+exports.assertTSTypeOperator = assertTSTypeOperator;
+exports.assertTSTypeParameter = assertTSTypeParameter;
+exports.assertTSTypeParameterDeclaration = assertTSTypeParameterDeclaration;
+exports.assertTSTypeParameterInstantiation = assertTSTypeParameterInstantiation;
+exports.assertTSTypePredicate = assertTSTypePredicate;
+exports.assertTSTypeQuery = assertTSTypeQuery;
+exports.assertTSTypeReference = assertTSTypeReference;
+exports.assertTSUndefinedKeyword = assertTSUndefinedKeyword;
+exports.assertTSUnionType = assertTSUnionType;
+exports.assertTSUnknownKeyword = assertTSUnknownKeyword;
+exports.assertTSVoidKeyword = assertTSVoidKeyword;
+exports.assertTaggedTemplateExpression = assertTaggedTemplateExpression;
+exports.assertTemplateElement = assertTemplateElement;
+exports.assertTemplateLiteral = assertTemplateLiteral;
+exports.assertTerminatorless = assertTerminatorless;
+exports.assertThisExpression = assertThisExpression;
+exports.assertThisTypeAnnotation = assertThisTypeAnnotation;
+exports.assertThrowStatement = assertThrowStatement;
+exports.assertTopicReference = assertTopicReference;
+exports.assertTryStatement = assertTryStatement;
+exports.assertTupleExpression = assertTupleExpression;
+exports.assertTupleTypeAnnotation = assertTupleTypeAnnotation;
+exports.assertTypeAlias = assertTypeAlias;
+exports.assertTypeAnnotation = assertTypeAnnotation;
+exports.assertTypeCastExpression = assertTypeCastExpression;
+exports.assertTypeParameter = assertTypeParameter;
+exports.assertTypeParameterDeclaration = assertTypeParameterDeclaration;
+exports.assertTypeParameterInstantiation = assertTypeParameterInstantiation;
+exports.assertTypeScript = assertTypeScript;
+exports.assertTypeofTypeAnnotation = assertTypeofTypeAnnotation;
+exports.assertUnaryExpression = assertUnaryExpression;
+exports.assertUnaryLike = assertUnaryLike;
+exports.assertUnionTypeAnnotation = assertUnionTypeAnnotation;
+exports.assertUpdateExpression = assertUpdateExpression;
+exports.assertUserWhitespacable = assertUserWhitespacable;
+exports.assertV8IntrinsicIdentifier = assertV8IntrinsicIdentifier;
+exports.assertVariableDeclaration = assertVariableDeclaration;
+exports.assertVariableDeclarator = assertVariableDeclarator;
+exports.assertVariance = assertVariance;
+exports.assertVoidPattern = assertVoidPattern;
+exports.assertVoidTypeAnnotation = assertVoidTypeAnnotation;
+exports.assertWhile = assertWhile;
+exports.assertWhileStatement = assertWhileStatement;
+exports.assertWithStatement = assertWithStatement;
+exports.assertYieldExpression = assertYieldExpression;
+var _is = require("../../validators/is.js");
+var _deprecationWarning = require("../../utils/deprecationWarning.js");
+function assert(type, node, opts) {
+  if (!(0, _is.default)(type, node, opts)) {
+    throw new Error(`Expected type "${type}" with option ${JSON.stringify(opts)}, ` + `but instead got "${node.type}".`);
+  }
+}
+function assertArrayExpression(node, opts) {
+  assert("ArrayExpression", node, opts);
+}
+function assertAssignmentExpression(node, opts) {
+  assert("AssignmentExpression", node, opts);
+}
+function assertBinaryExpression(node, opts) {
+  assert("BinaryExpression", node, opts);
+}
+function assertInterpreterDirective(node, opts) {
+  assert("InterpreterDirective", node, opts);
+}
+function assertDirective(node, opts) {
+  assert("Directive", node, opts);
+}
+function assertDirectiveLiteral(node, opts) {
+  assert("DirectiveLiteral", node, opts);
+}
+function assertBlockStatement(node, opts) {
+  assert("BlockStatement", node, opts);
+}
+function assertBreakStatement(node, opts) {
+  assert("BreakStatement", node, opts);
+}
+function assertCallExpression(node, opts) {
+  assert("CallExpression", node, opts);
+}
+function assertCatchClause(node, opts) {
+  assert("CatchClause", node, opts);
+}
+function assertConditionalExpression(node, opts) {
+  assert("ConditionalExpression", node, opts);
+}
+function assertContinueStatement(node, opts) {
+  assert("ContinueStatement", node, opts);
+}
+function assertDebuggerStatement(node, opts) {
+  assert("DebuggerStatement", node, opts);
+}
+function assertDoWhileStatement(node, opts) {
+  assert("DoWhileStatement", node, opts);
+}
+function assertEmptyStatement(node, opts) {
+  assert("EmptyStatement", node, opts);
+}
+function assertExpressionStatement(node, opts) {
+  assert("ExpressionStatement", node, opts);
+}
+function assertFile(node, opts) {
+  assert("File", node, opts);
+}
+function assertForInStatement(node, opts) {
+  assert("ForInStatement", node, opts);
+}
+function assertForStatement(node, opts) {
+  assert("ForStatement", node, opts);
+}
+function assertFunctionDeclaration(node, opts) {
+  assert("FunctionDeclaration", node, opts);
+}
+function assertFunctionExpression(node, opts) {
+  assert("FunctionExpression", node, opts);
+}
+function assertIdentifier(node, opts) {
+  assert("Identifier", node, opts);
+}
+function assertIfStatement(node, opts) {
+  assert("IfStatement", node, opts);
+}
+function assertLabeledStatement(node, opts) {
+  assert("LabeledStatement", node, opts);
+}
+function assertStringLiteral(node, opts) {
+  assert("StringLiteral", node, opts);
+}
+function assertNumericLiteral(node, opts) {
+  assert("NumericLiteral", node, opts);
+}
+function assertNullLiteral(node, opts) {
+  assert("NullLiteral", node, opts);
+}
+function assertBooleanLiteral(node, opts) {
+  assert("BooleanLiteral", node, opts);
+}
+function assertRegExpLiteral(node, opts) {
+  assert("RegExpLiteral", node, opts);
+}
+function assertLogicalExpression(node, opts) {
+  assert("LogicalExpression", node, opts);
+}
+function assertMemberExpression(node, opts) {
+  assert("MemberExpression", node, opts);
+}
+function assertNewExpression(node, opts) {
+  assert("NewExpression", node, opts);
+}
+function assertProgram(node, opts) {
+  assert("Program", node, opts);
+}
+function assertObjectExpression(node, opts) {
+  assert("ObjectExpression", node, opts);
+}
+function assertObjectMethod(node, opts) {
+  assert("ObjectMethod", node, opts);
+}
+function assertObjectProperty(node, opts) {
+  assert("ObjectProperty", node, opts);
+}
+function assertRestElement(node, opts) {
+  assert("RestElement", node, opts);
+}
+function assertReturnStatement(node, opts) {
+  assert("ReturnStatement", node, opts);
+}
+function assertSequenceExpression(node, opts) {
+  assert("SequenceExpression", node, opts);
+}
+function assertParenthesizedExpression(node, opts) {
+  assert("ParenthesizedExpression", node, opts);
+}
+function assertSwitchCase(node, opts) {
+  assert("SwitchCase", node, opts);
+}
+function assertSwitchStatement(node, opts) {
+  assert("SwitchStatement", node, opts);
+}
+function assertThisExpression(node, opts) {
+  assert("ThisExpression", node, opts);
+}
+function assertThrowStatement(node, opts) {
+  assert("ThrowStatement", node, opts);
+}
+function assertTryStatement(node, opts) {
+  assert("TryStatement", node, opts);
+}
+function assertUnaryExpression(node, opts) {
+  assert("UnaryExpression", node, opts);
+}
+function assertUpdateExpression(node, opts) {
+  assert("UpdateExpression", node, opts);
+}
+function assertVariableDeclaration(node, opts) {
+  assert("VariableDeclaration", node, opts);
+}
+function assertVariableDeclarator(node, opts) {
+  assert("VariableDeclarator", node, opts);
+}
+function assertWhileStatement(node, opts) {
+  assert("WhileStatement", node, opts);
+}
+function assertWithStatement(node, opts) {
+  assert("WithStatement", node, opts);
+}
+function assertAssignmentPattern(node, opts) {
+  assert("AssignmentPattern", node, opts);
+}
+function assertArrayPattern(node, opts) {
+  assert("ArrayPattern", node, opts);
+}
+function assertArrowFunctionExpression(node, opts) {
+  assert("ArrowFunctionExpression", node, opts);
+}
+function assertClassBody(node, opts) {
+  assert("ClassBody", node, opts);
+}
+function assertClassExpression(node, opts) {
+  assert("ClassExpression", node, opts);
+}
+function assertClassDeclaration(node, opts) {
+  assert("ClassDeclaration", node, opts);
+}
+function assertExportAllDeclaration(node, opts) {
+  assert("ExportAllDeclaration", node, opts);
+}
+function assertExportDefaultDeclaration(node, opts) {
+  assert("ExportDefaultDeclaration", node, opts);
+}
+function assertExportNamedDeclaration(node, opts) {
+  assert("ExportNamedDeclaration", node, opts);
+}
+function assertExportSpecifier(node, opts) {
+  assert("ExportSpecifier", node, opts);
+}
+function assertForOfStatement(node, opts) {
+  assert("ForOfStatement", node, opts);
+}
+function assertImportDeclaration(node, opts) {
+  assert("ImportDeclaration", node, opts);
+}
+function assertImportDefaultSpecifier(node, opts) {
+  assert("ImportDefaultSpecifier", node, opts);
+}
+function assertImportNamespaceSpecifier(node, opts) {
+  assert("ImportNamespaceSpecifier", node, opts);
+}
+function assertImportSpecifier(node, opts) {
+  assert("ImportSpecifier", node, opts);
+}
+function assertImportExpression(node, opts) {
+  assert("ImportExpression", node, opts);
+}
+function assertMetaProperty(node, opts) {
+  assert("MetaProperty", node, opts);
+}
+function assertClassMethod(node, opts) {
+  assert("ClassMethod", node, opts);
+}
+function assertObjectPattern(node, opts) {
+  assert("ObjectPattern", node, opts);
+}
+function assertSpreadElement(node, opts) {
+  assert("SpreadElement", node, opts);
+}
+function assertSuper(node, opts) {
+  assert("Super", node, opts);
+}
+function assertTaggedTemplateExpression(node, opts) {
+  assert("TaggedTemplateExpression", node, opts);
+}
+function assertTemplateElement(node, opts) {
+  assert("TemplateElement", node, opts);
+}
+function assertTemplateLiteral(node, opts) {
+  assert("TemplateLiteral", node, opts);
+}
+function assertYieldExpression(node, opts) {
+  assert("YieldExpression", node, opts);
+}
+function assertAwaitExpression(node, opts) {
+  assert("AwaitExpression", node, opts);
+}
+function assertImport(node, opts) {
+  assert("Import", node, opts);
+}
+function assertBigIntLiteral(node, opts) {
+  assert("BigIntLiteral", node, opts);
+}
+function assertExportNamespaceSpecifier(node, opts) {
+  assert("ExportNamespaceSpecifier", node, opts);
+}
+function assertOptionalMemberExpression(node, opts) {
+  assert("OptionalMemberExpression", node, opts);
+}
+function assertOptionalCallExpression(node, opts) {
+  assert("OptionalCallExpression", node, opts);
+}
+function assertClassProperty(node, opts) {
+  assert("ClassProperty", node, opts);
+}
+function assertClassAccessorProperty(node, opts) {
+  assert("ClassAccessorProperty", node, opts);
+}
+function assertClassPrivateProperty(node, opts) {
+  assert("ClassPrivateProperty", node, opts);
+}
+function assertClassPrivateMethod(node, opts) {
+  assert("ClassPrivateMethod", node, opts);
+}
+function assertPrivateName(node, opts) {
+  assert("PrivateName", node, opts);
+}
+function assertStaticBlock(node, opts) {
+  assert("StaticBlock", node, opts);
+}
+function assertImportAttribute(node, opts) {
+  assert("ImportAttribute", node, opts);
+}
+function assertAnyTypeAnnotation(node, opts) {
+  assert("AnyTypeAnnotation", node, opts);
+}
+function assertArrayTypeAnnotation(node, opts) {
+  assert("ArrayTypeAnnotation", node, opts);
+}
+function assertBooleanTypeAnnotation(node, opts) {
+  assert("BooleanTypeAnnotation", node, opts);
+}
+function assertBooleanLiteralTypeAnnotation(node, opts) {
+  assert("BooleanLiteralTypeAnnotation", node, opts);
+}
+function assertNullLiteralTypeAnnotation(node, opts) {
+  assert("NullLiteralTypeAnnotation", node, opts);
+}
+function assertClassImplements(node, opts) {
+  assert("ClassImplements", node, opts);
+}
+function assertDeclareClass(node, opts) {
+  assert("DeclareClass", node, opts);
+}
+function assertDeclareFunction(node, opts) {
+  assert("DeclareFunction", node, opts);
+}
+function assertDeclareInterface(node, opts) {
+  assert("DeclareInterface", node, opts);
+}
+function assertDeclareModule(node, opts) {
+  assert("DeclareModule", node, opts);
+}
+function assertDeclareModuleExports(node, opts) {
+  assert("DeclareModuleExports", node, opts);
+}
+function assertDeclareTypeAlias(node, opts) {
+  assert("DeclareTypeAlias", node, opts);
+}
+function assertDeclareOpaqueType(node, opts) {
+  assert("DeclareOpaqueType", node, opts);
+}
+function assertDeclareVariable(node, opts) {
+  assert("DeclareVariable", node, opts);
+}
+function assertDeclareExportDeclaration(node, opts) {
+  assert("DeclareExportDeclaration", node, opts);
+}
+function assertDeclareExportAllDeclaration(node, opts) {
+  assert("DeclareExportAllDeclaration", node, opts);
+}
+function assertDeclaredPredicate(node, opts) {
+  assert("DeclaredPredicate", node, opts);
+}
+function assertExistsTypeAnnotation(node, opts) {
+  assert("ExistsTypeAnnotation", node, opts);
+}
+function assertFunctionTypeAnnotation(node, opts) {
+  assert("FunctionTypeAnnotation", node, opts);
+}
+function assertFunctionTypeParam(node, opts) {
+  assert("FunctionTypeParam", node, opts);
+}
+function assertGenericTypeAnnotation(node, opts) {
+  assert("GenericTypeAnnotation", node, opts);
+}
+function assertInferredPredicate(node, opts) {
+  assert("InferredPredicate", node, opts);
+}
+function assertInterfaceExtends(node, opts) {
+  assert("InterfaceExtends", node, opts);
+}
+function assertInterfaceDeclaration(node, opts) {
+  assert("InterfaceDeclaration", node, opts);
+}
+function assertInterfaceTypeAnnotation(node, opts) {
+  assert("InterfaceTypeAnnotation", node, opts);
+}
+function assertIntersectionTypeAnnotation(node, opts) {
+  assert("IntersectionTypeAnnotation", node, opts);
+}
+function assertMixedTypeAnnotation(node, opts) {
+  assert("MixedTypeAnnotation", node, opts);
+}
+function assertEmptyTypeAnnotation(node, opts) {
+  assert("EmptyTypeAnnotation", node, opts);
+}
+function assertNullableTypeAnnotation(node, opts) {
+  assert("NullableTypeAnnotation", node, opts);
+}
+function assertNumberLiteralTypeAnnotation(node, opts) {
+  assert("NumberLiteralTypeAnnotation", node, opts);
+}
+function assertNumberTypeAnnotation(node, opts) {
+  assert("NumberTypeAnnotation", node, opts);
+}
+function assertObjectTypeAnnotation(node, opts) {
+  assert("ObjectTypeAnnotation", node, opts);
+}
+function assertObjectTypeInternalSlot(node, opts) {
+  assert("ObjectTypeInternalSlot", node, opts);
+}
+function assertObjectTypeCallProperty(node, opts) {
+  assert("ObjectTypeCallProperty", node, opts);
+}
+function assertObjectTypeIndexer(node, opts) {
+  assert("ObjectTypeIndexer", node, opts);
+}
+function assertObjectTypeProperty(node, opts) {
+  assert("ObjectTypeProperty", node, opts);
+}
+function assertObjectTypeSpreadProperty(node, opts) {
+  assert("ObjectTypeSpreadProperty", node, opts);
+}
+function assertOpaqueType(node, opts) {
+  assert("OpaqueType", node, opts);
+}
+function assertQualifiedTypeIdentifier(node, opts) {
+  assert("QualifiedTypeIdentifier", node, opts);
+}
+function assertStringLiteralTypeAnnotation(node, opts) {
+  assert("StringLiteralTypeAnnotation", node, opts);
+}
+function assertStringTypeAnnotation(node, opts) {
+  assert("StringTypeAnnotation", node, opts);
+}
+function assertSymbolTypeAnnotation(node, opts) {
+  assert("SymbolTypeAnnotation", node, opts);
+}
+function assertThisTypeAnnotation(node, opts) {
+  assert("ThisTypeAnnotation", node, opts);
+}
+function assertTupleTypeAnnotation(node, opts) {
+  assert("TupleTypeAnnotation", node, opts);
+}
+function assertTypeofTypeAnnotation(node, opts) {
+  assert("TypeofTypeAnnotation", node, opts);
+}
+function assertTypeAlias(node, opts) {
+  assert("TypeAlias", node, opts);
+}
+function assertTypeAnnotation(node, opts) {
+  assert("TypeAnnotation", node, opts);
+}
+function assertTypeCastExpression(node, opts) {
+  assert("TypeCastExpression", node, opts);
+}
+function assertTypeParameter(node, opts) {
+  assert("TypeParameter", node, opts);
+}
+function assertTypeParameterDeclaration(node, opts) {
+  assert("TypeParameterDeclaration", node, opts);
+}
+function assertTypeParameterInstantiation(node, opts) {
+  assert("TypeParameterInstantiation", node, opts);
+}
+function assertUnionTypeAnnotation(node, opts) {
+  assert("UnionTypeAnnotation", node, opts);
+}
+function assertVariance(node, opts) {
+  assert("Variance", node, opts);
+}
+function assertVoidTypeAnnotation(node, opts) {
+  assert("VoidTypeAnnotation", node, opts);
+}
+function assertEnumDeclaration(node, opts) {
+  assert("EnumDeclaration", node, opts);
+}
+function assertEnumBooleanBody(node, opts) {
+  assert("EnumBooleanBody", node, opts);
+}
+function assertEnumNumberBody(node, opts) {
+  assert("EnumNumberBody", node, opts);
+}
+function assertEnumStringBody(node, opts) {
+  assert("EnumStringBody", node, opts);
+}
+function assertEnumSymbolBody(node, opts) {
+  assert("EnumSymbolBody", node, opts);
+}
+function assertEnumBooleanMember(node, opts) {
+  assert("EnumBooleanMember", node, opts);
+}
+function assertEnumNumberMember(node, opts) {
+  assert("EnumNumberMember", node, opts);
+}
+function assertEnumStringMember(node, opts) {
+  assert("EnumStringMember", node, opts);
+}
+function assertEnumDefaultedMember(node, opts) {
+  assert("EnumDefaultedMember", node, opts);
+}
+function assertIndexedAccessType(node, opts) {
+  assert("IndexedAccessType", node, opts);
+}
+function assertOptionalIndexedAccessType(node, opts) {
+  assert("OptionalIndexedAccessType", node, opts);
+}
+function assertJSXAttribute(node, opts) {
+  assert("JSXAttribute", node, opts);
+}
+function assertJSXClosingElement(node, opts) {
+  assert("JSXClosingElement", node, opts);
+}
+function assertJSXElement(node, opts) {
+  assert("JSXElement", node, opts);
+}
+function assertJSXEmptyExpression(node, opts) {
+  assert("JSXEmptyExpression", node, opts);
+}
+function assertJSXExpressionContainer(node, opts) {
+  assert("JSXExpressionContainer", node, opts);
+}
+function assertJSXSpreadChild(node, opts) {
+  assert("JSXSpreadChild", node, opts);
+}
+function assertJSXIdentifier(node, opts) {
+  assert("JSXIdentifier", node, opts);
+}
+function assertJSXMemberExpression(node, opts) {
+  assert("JSXMemberExpression", node, opts);
+}
+function assertJSXNamespacedName(node, opts) {
+  assert("JSXNamespacedName", node, opts);
+}
+function assertJSXOpeningElement(node, opts) {
+  assert("JSXOpeningElement", node, opts);
+}
+function assertJSXSpreadAttribute(node, opts) {
+  assert("JSXSpreadAttribute", node, opts);
+}
+function assertJSXText(node, opts) {
+  assert("JSXText", node, opts);
+}
+function assertJSXFragment(node, opts) {
+  assert("JSXFragment", node, opts);
+}
+function assertJSXOpeningFragment(node, opts) {
+  assert("JSXOpeningFragment", node, opts);
+}
+function assertJSXClosingFragment(node, opts) {
+  assert("JSXClosingFragment", node, opts);
+}
+function assertNoop(node, opts) {
+  assert("Noop", node, opts);
+}
+function assertPlaceholder(node, opts) {
+  assert("Placeholder", node, opts);
+}
+function assertV8IntrinsicIdentifier(node, opts) {
+  assert("V8IntrinsicIdentifier", node, opts);
+}
+function assertArgumentPlaceholder(node, opts) {
+  assert("ArgumentPlaceholder", node, opts);
+}
+function assertBindExpression(node, opts) {
+  assert("BindExpression", node, opts);
+}
+function assertDecorator(node, opts) {
+  assert("Decorator", node, opts);
+}
+function assertDoExpression(node, opts) {
+  assert("DoExpression", node, opts);
+}
+function assertExportDefaultSpecifier(node, opts) {
+  assert("ExportDefaultSpecifier", node, opts);
+}
+function assertRecordExpression(node, opts) {
+  assert("RecordExpression", node, opts);
+}
+function assertTupleExpression(node, opts) {
+  assert("TupleExpression", node, opts);
+}
+function assertDecimalLiteral(node, opts) {
+  assert("DecimalLiteral", node, opts);
+}
+function assertModuleExpression(node, opts) {
+  assert("ModuleExpression", node, opts);
+}
+function assertTopicReference(node, opts) {
+  assert("TopicReference", node, opts);
+}
+function assertPipelineTopicExpression(node, opts) {
+  assert("PipelineTopicExpression", node, opts);
+}
+function assertPipelineBareFunction(node, opts) {
+  assert("PipelineBareFunction", node, opts);
+}
+function assertPipelinePrimaryTopicReference(node, opts) {
+  assert("PipelinePrimaryTopicReference", node, opts);
+}
+function assertVoidPattern(node, opts) {
+  assert("VoidPattern", node, opts);
+}
+function assertTSParameterProperty(node, opts) {
+  assert("TSParameterProperty", node, opts);
+}
+function assertTSDeclareFunction(node, opts) {
+  assert("TSDeclareFunction", node, opts);
+}
+function assertTSDeclareMethod(node, opts) {
+  assert("TSDeclareMethod", node, opts);
+}
+function assertTSQualifiedName(node, opts) {
+  assert("TSQualifiedName", node, opts);
+}
+function assertTSCallSignatureDeclaration(node, opts) {
+  assert("TSCallSignatureDeclaration", node, opts);
+}
+function assertTSConstructSignatureDeclaration(node, opts) {
+  assert("TSConstructSignatureDeclaration", node, opts);
+}
+function assertTSPropertySignature(node, opts) {
+  assert("TSPropertySignature", node, opts);
+}
+function assertTSMethodSignature(node, opts) {
+  assert("TSMethodSignature", node, opts);
+}
+function assertTSIndexSignature(node, opts) {
+  assert("TSIndexSignature", node, opts);
+}
+function assertTSAnyKeyword(node, opts) {
+  assert("TSAnyKeyword", node, opts);
+}
+function assertTSBooleanKeyword(node, opts) {
+  assert("TSBooleanKeyword", node, opts);
+}
+function assertTSBigIntKeyword(node, opts) {
+  assert("TSBigIntKeyword", node, opts);
+}
+function assertTSIntrinsicKeyword(node, opts) {
+  assert("TSIntrinsicKeyword", node, opts);
+}
+function assertTSNeverKeyword(node, opts) {
+  assert("TSNeverKeyword", node, opts);
+}
+function assertTSNullKeyword(node, opts) {
+  assert("TSNullKeyword", node, opts);
+}
+function assertTSNumberKeyword(node, opts) {
+  assert("TSNumberKeyword", node, opts);
+}
+function assertTSObjectKeyword(node, opts) {
+  assert("TSObjectKeyword", node, opts);
+}
+function assertTSStringKeyword(node, opts) {
+  assert("TSStringKeyword", node, opts);
+}
+function assertTSSymbolKeyword(node, opts) {
+  assert("TSSymbolKeyword", node, opts);
+}
+function assertTSUndefinedKeyword(node, opts) {
+  assert("TSUndefinedKeyword", node, opts);
+}
+function assertTSUnknownKeyword(node, opts) {
+  assert("TSUnknownKeyword", node, opts);
+}
+function assertTSVoidKeyword(node, opts) {
+  assert("TSVoidKeyword", node, opts);
+}
+function assertTSThisType(node, opts) {
+  assert("TSThisType", node, opts);
+}
+function assertTSFunctionType(node, opts) {
+  assert("TSFunctionType", node, opts);
+}
+function assertTSConstructorType(node, opts) {
+  assert("TSConstructorType", node, opts);
+}
+function assertTSTypeReference(node, opts) {
+  assert("TSTypeReference", node, opts);
+}
+function assertTSTypePredicate(node, opts) {
+  assert("TSTypePredicate", node, opts);
+}
+function assertTSTypeQuery(node, opts) {
+  assert("TSTypeQuery", node, opts);
+}
+function assertTSTypeLiteral(node, opts) {
+  assert("TSTypeLiteral", node, opts);
+}
+function assertTSArrayType(node, opts) {
+  assert("TSArrayType", node, opts);
+}
+function assertTSTupleType(node, opts) {
+  assert("TSTupleType", node, opts);
+}
+function assertTSOptionalType(node, opts) {
+  assert("TSOptionalType", node, opts);
+}
+function assertTSRestType(node, opts) {
+  assert("TSRestType", node, opts);
+}
+function assertTSNamedTupleMember(node, opts) {
+  assert("TSNamedTupleMember", node, opts);
+}
+function assertTSUnionType(node, opts) {
+  assert("TSUnionType", node, opts);
+}
+function assertTSIntersectionType(node, opts) {
+  assert("TSIntersectionType", node, opts);
+}
+function assertTSConditionalType(node, opts) {
+  assert("TSConditionalType", node, opts);
+}
+function assertTSInferType(node, opts) {
+  assert("TSInferType", node, opts);
+}
+function assertTSParenthesizedType(node, opts) {
+  assert("TSParenthesizedType", node, opts);
+}
+function assertTSTypeOperator(node, opts) {
+  assert("TSTypeOperator", node, opts);
+}
+function assertTSIndexedAccessType(node, opts) {
+  assert("TSIndexedAccessType", node, opts);
+}
+function assertTSMappedType(node, opts) {
+  assert("TSMappedType", node, opts);
+}
+function assertTSTemplateLiteralType(node, opts) {
+  assert("TSTemplateLiteralType", node, opts);
+}
+function assertTSLiteralType(node, opts) {
+  assert("TSLiteralType", node, opts);
+}
+function assertTSExpressionWithTypeArguments(node, opts) {
+  assert("TSExpressionWithTypeArguments", node, opts);
+}
+function assertTSInterfaceDeclaration(node, opts) {
+  assert("TSInterfaceDeclaration", node, opts);
+}
+function assertTSInterfaceBody(node, opts) {
+  assert("TSInterfaceBody", node, opts);
+}
+function assertTSTypeAliasDeclaration(node, opts) {
+  assert("TSTypeAliasDeclaration", node, opts);
+}
+function assertTSInstantiationExpression(node, opts) {
+  assert("TSInstantiationExpression", node, opts);
+}
+function assertTSAsExpression(node, opts) {
+  assert("TSAsExpression", node, opts);
+}
+function assertTSSatisfiesExpression(node, opts) {
+  assert("TSSatisfiesExpression", node, opts);
+}
+function assertTSTypeAssertion(node, opts) {
+  assert("TSTypeAssertion", node, opts);
+}
+function assertTSEnumBody(node, opts) {
+  assert("TSEnumBody", node, opts);
+}
+function assertTSEnumDeclaration(node, opts) {
+  assert("TSEnumDeclaration", node, opts);
+}
+function assertTSEnumMember(node, opts) {
+  assert("TSEnumMember", node, opts);
+}
+function assertTSModuleDeclaration(node, opts) {
+  assert("TSModuleDeclaration", node, opts);
+}
+function assertTSModuleBlock(node, opts) {
+  assert("TSModuleBlock", node, opts);
+}
+function assertTSImportType(node, opts) {
+  assert("TSImportType", node, opts);
+}
+function assertTSImportEqualsDeclaration(node, opts) {
+  assert("TSImportEqualsDeclaration", node, opts);
+}
+function assertTSExternalModuleReference(node, opts) {
+  assert("TSExternalModuleReference", node, opts);
+}
+function assertTSNonNullExpression(node, opts) {
+  assert("TSNonNullExpression", node, opts);
+}
+function assertTSExportAssignment(node, opts) {
+  assert("TSExportAssignment", node, opts);
+}
+function assertTSNamespaceExportDeclaration(node, opts) {
+  assert("TSNamespaceExportDeclaration", node, opts);
+}
+function assertTSTypeAnnotation(node, opts) {
+  assert("TSTypeAnnotation", node, opts);
+}
+function assertTSTypeParameterInstantiation(node, opts) {
+  assert("TSTypeParameterInstantiation", node, opts);
+}
+function assertTSTypeParameterDeclaration(node, opts) {
+  assert("TSTypeParameterDeclaration", node, opts);
+}
+function assertTSTypeParameter(node, opts) {
+  assert("TSTypeParameter", node, opts);
+}
+function assertStandardized(node, opts) {
+  assert("Standardized", node, opts);
+}
+function assertExpression(node, opts) {
+  assert("Expression", node, opts);
+}
+function assertBinary(node, opts) {
+  assert("Binary", node, opts);
+}
+function assertScopable(node, opts) {
+  assert("Scopable", node, opts);
+}
+function assertBlockParent(node, opts) {
+  assert("BlockParent", node, opts);
+}
+function assertBlock(node, opts) {
+  assert("Block", node, opts);
+}
+function assertStatement(node, opts) {
+  assert("Statement", node, opts);
+}
+function assertTerminatorless(node, opts) {
+  assert("Terminatorless", node, opts);
+}
+function assertCompletionStatement(node, opts) {
+  assert("CompletionStatement", node, opts);
+}
+function assertConditional(node, opts) {
+  assert("Conditional", node, opts);
+}
+function assertLoop(node, opts) {
+  assert("Loop", node, opts);
+}
+function assertWhile(node, opts) {
+  assert("While", node, opts);
+}
+function assertExpressionWrapper(node, opts) {
+  assert("ExpressionWrapper", node, opts);
+}
+function assertFor(node, opts) {
+  assert("For", node, opts);
+}
+function assertForXStatement(node, opts) {
+  assert("ForXStatement", node, opts);
+}
+function assertFunction(node, opts) {
+  assert("Function", node, opts);
+}
+function assertFunctionParent(node, opts) {
+  assert("FunctionParent", node, opts);
+}
+function assertPureish(node, opts) {
+  assert("Pureish", node, opts);
+}
+function assertDeclaration(node, opts) {
+  assert("Declaration", node, opts);
+}
+function assertFunctionParameter(node, opts) {
+  assert("FunctionParameter", node, opts);
+}
+function assertPatternLike(node, opts) {
+  assert("PatternLike", node, opts);
+}
+function assertLVal(node, opts) {
+  assert("LVal", node, opts);
+}
+function assertTSEntityName(node, opts) {
+  assert("TSEntityName", node, opts);
+}
+function assertLiteral(node, opts) {
+  assert("Literal", node, opts);
+}
+function assertImmutable(node, opts) {
+  assert("Immutable", node, opts);
+}
+function assertUserWhitespacable(node, opts) {
+  assert("UserWhitespacable", node, opts);
+}
+function assertMethod(node, opts) {
+  assert("Method", node, opts);
+}
+function assertObjectMember(node, opts) {
+  assert("ObjectMember", node, opts);
+}
+function assertProperty(node, opts) {
+  assert("Property", node, opts);
+}
+function assertUnaryLike(node, opts) {
+  assert("UnaryLike", node, opts);
+}
+function assertPattern(node, opts) {
+  assert("Pattern", node, opts);
+}
+function assertClass(node, opts) {
+  assert("Class", node, opts);
+}
+function assertImportOrExportDeclaration(node, opts) {
+  assert("ImportOrExportDeclaration", node, opts);
+}
+function assertExportDeclaration(node, opts) {
+  assert("ExportDeclaration", node, opts);
+}
+function assertModuleSpecifier(node, opts) {
+  assert("ModuleSpecifier", node, opts);
+}
+function assertAccessor(node, opts) {
+  assert("Accessor", node, opts);
+}
+function assertPrivate(node, opts) {
+  assert("Private", node, opts);
+}
+function assertFlow(node, opts) {
+  assert("Flow", node, opts);
+}
+function assertFlowType(node, opts) {
+  assert("FlowType", node, opts);
+}
+function assertFlowBaseAnnotation(node, opts) {
+  assert("FlowBaseAnnotation", node, opts);
+}
+function assertFlowDeclaration(node, opts) {
+  assert("FlowDeclaration", node, opts);
+}
+function assertFlowPredicate(node, opts) {
+  assert("FlowPredicate", node, opts);
+}
+function assertEnumBody(node, opts) {
+  assert("EnumBody", node, opts);
+}
+function assertEnumMember(node, opts) {
+  assert("EnumMember", node, opts);
+}
+function assertJSX(node, opts) {
+  assert("JSX", node, opts);
+}
+function assertMiscellaneous(node, opts) {
+  assert("Miscellaneous", node, opts);
+}
+function assertTypeScript(node, opts) {
+  assert("TypeScript", node, opts);
+}
+function assertTSTypeElement(node, opts) {
+  assert("TSTypeElement", node, opts);
+}
+function assertTSType(node, opts) {
+  assert("TSType", node, opts);
+}
+function assertTSBaseType(node, opts) {
+  assert("TSBaseType", node, opts);
+}
+function assertNumberLiteral(node, opts) {
+  (0, _deprecationWarning.default)("assertNumberLiteral", "assertNumericLiteral");
+  assert("NumberLiteral", node, opts);
+}
+function assertRegexLiteral(node, opts) {
+  (0, _deprecationWarning.default)("assertRegexLiteral", "assertRegExpLiteral");
+  assert("RegexLiteral", node, opts);
+}
+function assertRestProperty(node, opts) {
+  (0, _deprecationWarning.default)("assertRestProperty", "assertRestElement");
+  assert("RestProperty", node, opts);
+}
+function assertSpreadProperty(node, opts) {
+  (0, _deprecationWarning.default)("assertSpreadProperty", "assertSpreadElement");
+  assert("SpreadProperty", node, opts);
+}
+function assertModuleDeclaration(node, opts) {
+  (0, _deprecationWarning.default)("assertModuleDeclaration", "assertImportOrExportDeclaration");
+  assert("ModuleDeclaration", node, opts);
+}
+
+//# sourceMappingURL=index.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/types/lib/asserts/generated/index.js.map


+ 3 - 0
node_modules/@babel/types/lib/ast-types/generated/index.js

@@ -0,0 +1,3 @@
+"use strict";
+
+//# sourceMappingURL=index.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/types/lib/ast-types/generated/index.js.map


+ 18 - 0
node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js

@@ -0,0 +1,18 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = createFlowUnionType;
+var _index = require("../generated/index.js");
+var _removeTypeDuplicates = require("../../modifications/flow/removeTypeDuplicates.js");
+function createFlowUnionType(types) {
+  const flattened = (0, _removeTypeDuplicates.default)(types);
+  if (flattened.length === 1) {
+    return flattened[0];
+  } else {
+    return (0, _index.unionTypeAnnotation)(flattened);
+  }
+}
+
+//# sourceMappingURL=createFlowUnionType.js.map

+ 1 - 0
node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js.map

@@ -0,0 +1 @@
+{"version":3,"names":["_index","require","_removeTypeDuplicates","createFlowUnionType","types","flattened","removeTypeDuplicates","length","unionTypeAnnotation"],"sources":["../../../src/builders/flow/createFlowUnionType.ts"],"sourcesContent":["import { unionTypeAnnotation } from \"../generated/index.ts\";\nimport removeTypeDuplicates from \"../../modifications/flow/removeTypeDuplicates.ts\";\nimport type * as t from \"../../index.ts\";\n\n/**\n * Takes an array of `types` and flattens them, removing duplicates and\n * returns a `UnionTypeAnnotation` node containing them.\n */\nexport default function createFlowUnionType<T extends t.FlowType>(\n  types: [T] | T[],\n): T | t.UnionTypeAnnotation {\n  const flattened = removeTypeDuplicates(types);\n\n  if (flattened.length === 1) {\n    return flattened[0] as T;\n  } else {\n    return unionTypeAnnotation(flattened);\n  }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,qBAAA,GAAAD,OAAA;AAOe,SAASE,mBAAmBA,CACzCC,KAAgB,EACW;EAC3B,MAAMC,SAAS,GAAG,IAAAC,6BAAoB,EAACF,KAAK,CAAC;EAE7C,IAAIC,SAAS,CAACE,MAAM,KAAK,CAAC,EAAE;IAC1B,OAAOF,SAAS,CAAC,CAAC,CAAC;EACrB,CAAC,MAAM;IACL,OAAO,IAAAG,0BAAmB,EAACH,SAAS,CAAC;EACvC;AACF","ignoreList":[]}

+ 31 - 0
node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js

@@ -0,0 +1,31 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = void 0;
+var _index = require("../generated/index.js");
+var _default = exports.default = createTypeAnnotationBasedOnTypeof;
+function createTypeAnnotationBasedOnTypeof(type) {
+  switch (type) {
+    case "string":
+      return (0, _index.stringTypeAnnotation)();
+    case "number":
+      return (0, _index.numberTypeAnnotation)();
+    case "undefined":
+      return (0, _index.voidTypeAnnotation)();
+    case "boolean":
+      return (0, _index.booleanTypeAnnotation)();
+    case "function":
+      return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Function"));
+    case "object":
+      return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Object"));
+    case "symbol":
+      return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Symbol"));
+    case "bigint":
+      return (0, _index.anyTypeAnnotation)();
+  }
+  throw new Error("Invalid typeof value: " + type);
+}
+
+//# sourceMappingURL=createTypeAnnotationBasedOnTypeof.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js.map


+ 29 - 0
node_modules/@babel/types/lib/builders/generated/index.js

@@ -0,0 +1,29 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+var _lowercase = require("./lowercase.js");
+Object.keys(_lowercase).forEach(function (key) {
+  if (key === "default" || key === "__esModule") return;
+  if (key in exports && exports[key] === _lowercase[key]) return;
+  Object.defineProperty(exports, key, {
+    enumerable: true,
+    get: function () {
+      return _lowercase[key];
+    }
+  });
+});
+var _uppercase = require("./uppercase.js");
+Object.keys(_uppercase).forEach(function (key) {
+  if (key === "default" || key === "__esModule") return;
+  if (key in exports && exports[key] === _uppercase[key]) return;
+  Object.defineProperty(exports, key, {
+    enumerable: true,
+    get: function () {
+      return _uppercase[key];
+    }
+  });
+});
+
+//# sourceMappingURL=index.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/types/lib/builders/generated/index.js.map


+ 2903 - 0
node_modules/@babel/types/lib/builders/generated/lowercase.js

@@ -0,0 +1,2903 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.anyTypeAnnotation = anyTypeAnnotation;
+exports.argumentPlaceholder = argumentPlaceholder;
+exports.arrayExpression = arrayExpression;
+exports.arrayPattern = arrayPattern;
+exports.arrayTypeAnnotation = arrayTypeAnnotation;
+exports.arrowFunctionExpression = arrowFunctionExpression;
+exports.assignmentExpression = assignmentExpression;
+exports.assignmentPattern = assignmentPattern;
+exports.awaitExpression = awaitExpression;
+exports.bigIntLiteral = bigIntLiteral;
+exports.binaryExpression = binaryExpression;
+exports.bindExpression = bindExpression;
+exports.blockStatement = blockStatement;
+exports.booleanLiteral = booleanLiteral;
+exports.booleanLiteralTypeAnnotation = booleanLiteralTypeAnnotation;
+exports.booleanTypeAnnotation = booleanTypeAnnotation;
+exports.breakStatement = breakStatement;
+exports.callExpression = callExpression;
+exports.catchClause = catchClause;
+exports.classAccessorProperty = classAccessorProperty;
+exports.classBody = classBody;
+exports.classDeclaration = classDeclaration;
+exports.classExpression = classExpression;
+exports.classImplements = classImplements;
+exports.classMethod = classMethod;
+exports.classPrivateMethod = classPrivateMethod;
+exports.classPrivateProperty = classPrivateProperty;
+exports.classProperty = classProperty;
+exports.conditionalExpression = conditionalExpression;
+exports.continueStatement = continueStatement;
+exports.debuggerStatement = debuggerStatement;
+exports.decimalLiteral = decimalLiteral;
+exports.declareClass = declareClass;
+exports.declareExportAllDeclaration = declareExportAllDeclaration;
+exports.declareExportDeclaration = declareExportDeclaration;
+exports.declareFunction = declareFunction;
+exports.declareInterface = declareInterface;
+exports.declareModule = declareModule;
+exports.declareModuleExports = declareModuleExports;
+exports.declareOpaqueType = declareOpaqueType;
+exports.declareTypeAlias = declareTypeAlias;
+exports.declareVariable = declareVariable;
+exports.declaredPredicate = declaredPredicate;
+exports.decorator = decorator;
+exports.directive = directive;
+exports.directiveLiteral = directiveLiteral;
+exports.doExpression = doExpression;
+exports.doWhileStatement = doWhileStatement;
+exports.emptyStatement = emptyStatement;
+exports.emptyTypeAnnotation = emptyTypeAnnotation;
+exports.enumBooleanBody = enumBooleanBody;
+exports.enumBooleanMember = enumBooleanMember;
+exports.enumDeclaration = enumDeclaration;
+exports.enumDefaultedMember = enumDefaultedMember;
+exports.enumNumberBody = enumNumberBody;
+exports.enumNumberMember = enumNumberMember;
+exports.enumStringBody = enumStringBody;
+exports.enumStringMember = enumStringMember;
+exports.enumSymbolBody = enumSymbolBody;
+exports.existsTypeAnnotation = existsTypeAnnotation;
+exports.exportAllDeclaration = exportAllDeclaration;
+exports.exportDefaultDeclaration = exportDefaultDeclaration;
+exports.exportDefaultSpecifier = exportDefaultSpecifier;
+exports.exportNamedDeclaration = exportNamedDeclaration;
+exports.exportNamespaceSpecifier = exportNamespaceSpecifier;
+exports.exportSpecifier = exportSpecifier;
+exports.expressionStatement = expressionStatement;
+exports.file = file;
+exports.forInStatement = forInStatement;
+exports.forOfStatement = forOfStatement;
+exports.forStatement = forStatement;
+exports.functionDeclaration = functionDeclaration;
+exports.functionExpression = functionExpression;
+exports.functionTypeAnnotation = functionTypeAnnotation;
+exports.functionTypeParam = functionTypeParam;
+exports.genericTypeAnnotation = genericTypeAnnotation;
+exports.identifier = identifier;
+exports.ifStatement = ifStatement;
+exports.import = _import;
+exports.importAttribute = importAttribute;
+exports.importDeclaration = importDeclaration;
+exports.importDefaultSpecifier = importDefaultSpecifier;
+exports.importExpression = importExpression;
+exports.importNamespaceSpecifier = importNamespaceSpecifier;
+exports.importSpecifier = importSpecifier;
+exports.indexedAccessType = indexedAccessType;
+exports.inferredPredicate = inferredPredicate;
+exports.interfaceDeclaration = interfaceDeclaration;
+exports.interfaceExtends = interfaceExtends;
+exports.interfaceTypeAnnotation = interfaceTypeAnnotation;
+exports.interpreterDirective = interpreterDirective;
+exports.intersectionTypeAnnotation = intersectionTypeAnnotation;
+exports.jSXAttribute = exports.jsxAttribute = jsxAttribute;
+exports.jSXClosingElement = exports.jsxClosingElement = jsxClosingElement;
+exports.jSXClosingFragment = exports.jsxClosingFragment = jsxClosingFragment;
+exports.jSXElement = exports.jsxElement = jsxElement;
+exports.jSXEmptyExpression = exports.jsxEmptyExpression = jsxEmptyExpression;
+exports.jSXExpressionContainer = exports.jsxExpressionContainer = jsxExpressionContainer;
+exports.jSXFragment = exports.jsxFragment = jsxFragment;
+exports.jSXIdentifier = exports.jsxIdentifier = jsxIdentifier;
+exports.jSXMemberExpression = exports.jsxMemberExpression = jsxMemberExpression;
+exports.jSXNamespacedName = exports.jsxNamespacedName = jsxNamespacedName;
+exports.jSXOpeningElement = exports.jsxOpeningElement = jsxOpeningElement;
+exports.jSXOpeningFragment = exports.jsxOpeningFragment = jsxOpeningFragment;
+exports.jSXSpreadAttribute = exports.jsxSpreadAttribute = jsxSpreadAttribute;
+exports.jSXSpreadChild = exports.jsxSpreadChild = jsxSpreadChild;
+exports.jSXText = exports.jsxText = jsxText;
+exports.labeledStatement = labeledStatement;
+exports.logicalExpression = logicalExpression;
+exports.memberExpression = memberExpression;
+exports.metaProperty = metaProperty;
+exports.mixedTypeAnnotation = mixedTypeAnnotation;
+exports.moduleExpression = moduleExpression;
+exports.newExpression = newExpression;
+exports.noop = noop;
+exports.nullLiteral = nullLiteral;
+exports.nullLiteralTypeAnnotation = nullLiteralTypeAnnotation;
+exports.nullableTypeAnnotation = nullableTypeAnnotation;
+exports.numberLiteral = NumberLiteral;
+exports.numberLiteralTypeAnnotation = numberLiteralTypeAnnotation;
+exports.numberTypeAnnotation = numberTypeAnnotation;
+exports.numericLiteral = numericLiteral;
+exports.objectExpression = objectExpression;
+exports.objectMethod = objectMethod;
+exports.objectPattern = objectPattern;
+exports.objectProperty = objectProperty;
+exports.objectTypeAnnotation = objectTypeAnnotation;
+exports.objectTypeCallProperty = objectTypeCallProperty;
+exports.objectTypeIndexer = objectTypeIndexer;
+exports.objectTypeInternalSlot = objectTypeInternalSlot;
+exports.objectTypeProperty = objectTypeProperty;
+exports.objectTypeSpreadProperty = objectTypeSpreadProperty;
+exports.opaqueType = opaqueType;
+exports.optionalCallExpression = optionalCallExpression;
+exports.optionalIndexedAccessType = optionalIndexedAccessType;
+exports.optionalMemberExpression = optionalMemberExpression;
+exports.parenthesizedExpression = parenthesizedExpression;
+exports.pipelineBareFunction = pipelineBareFunction;
+exports.pipelinePrimaryTopicReference = pipelinePrimaryTopicReference;
+exports.pipelineTopicExpression = pipelineTopicExpression;
+exports.placeholder = placeholder;
+exports.privateName = privateName;
+exports.program = program;
+exports.qualifiedTypeIdentifier = qualifiedTypeIdentifier;
+exports.recordExpression = recordExpression;
+exports.regExpLiteral = regExpLiteral;
+exports.regexLiteral = RegexLiteral;
+exports.restElement = restElement;
+exports.restProperty = RestProperty;
+exports.returnStatement = returnStatement;
+exports.sequenceExpression = sequenceExpression;
+exports.spreadElement = spreadElement;
+exports.spreadProperty = SpreadProperty;
+exports.staticBlock = staticBlock;
+exports.stringLiteral = stringLiteral;
+exports.stringLiteralTypeAnnotation = stringLiteralTypeAnnotation;
+exports.stringTypeAnnotation = stringTypeAnnotation;
+exports.super = _super;
+exports.switchCase = switchCase;
+exports.switchStatement = switchStatement;
+exports.symbolTypeAnnotation = symbolTypeAnnotation;
+exports.taggedTemplateExpression = taggedTemplateExpression;
+exports.templateElement = templateElement;
+exports.templateLiteral = templateLiteral;
+exports.thisExpression = thisExpression;
+exports.thisTypeAnnotation = thisTypeAnnotation;
+exports.throwStatement = throwStatement;
+exports.topicReference = topicReference;
+exports.tryStatement = tryStatement;
+exports.tSAnyKeyword = exports.tsAnyKeyword = tsAnyKeyword;
+exports.tSArrayType = exports.tsArrayType = tsArrayType;
+exports.tSAsExpression = exports.tsAsExpression = tsAsExpression;
+exports.tSBigIntKeyword = exports.tsBigIntKeyword = tsBigIntKeyword;
+exports.tSBooleanKeyword = exports.tsBooleanKeyword = tsBooleanKeyword;
+exports.tSCallSignatureDeclaration = exports.tsCallSignatureDeclaration = tsCallSignatureDeclaration;
+exports.tSConditionalType = exports.tsConditionalType = tsConditionalType;
+exports.tSConstructSignatureDeclaration = exports.tsConstructSignatureDeclaration = tsConstructSignatureDeclaration;
+exports.tSConstructorType = exports.tsConstructorType = tsConstructorType;
+exports.tSDeclareFunction = exports.tsDeclareFunction = tsDeclareFunction;
+exports.tSDeclareMethod = exports.tsDeclareMethod = tsDeclareMethod;
+exports.tSEnumBody = exports.tsEnumBody = tsEnumBody;
+exports.tSEnumDeclaration = exports.tsEnumDeclaration = tsEnumDeclaration;
+exports.tSEnumMember = exports.tsEnumMember = tsEnumMember;
+exports.tSExportAssignment = exports.tsExportAssignment = tsExportAssignment;
+exports.tSExpressionWithTypeArguments = exports.tsExpressionWithTypeArguments = tsExpressionWithTypeArguments;
+exports.tSExternalModuleReference = exports.tsExternalModuleReference = tsExternalModuleReference;
+exports.tSFunctionType = exports.tsFunctionType = tsFunctionType;
+exports.tSImportEqualsDeclaration = exports.tsImportEqualsDeclaration = tsImportEqualsDeclaration;
+exports.tSImportType = exports.tsImportType = tsImportType;
+exports.tSIndexSignature = exports.tsIndexSignature = tsIndexSignature;
+exports.tSIndexedAccessType = exports.tsIndexedAccessType = tsIndexedAccessType;
+exports.tSInferType = exports.tsInferType = tsInferType;
+exports.tSInstantiationExpression = exports.tsInstantiationExpression = tsInstantiationExpression;
+exports.tSInterfaceBody = exports.tsInterfaceBody = tsInterfaceBody;
+exports.tSInterfaceDeclaration = exports.tsInterfaceDeclaration = tsInterfaceDeclaration;
+exports.tSIntersectionType = exports.tsIntersectionType = tsIntersectionType;
+exports.tSIntrinsicKeyword = exports.tsIntrinsicKeyword = tsIntrinsicKeyword;
+exports.tSLiteralType = exports.tsLiteralType = tsLiteralType;
+exports.tSMappedType = exports.tsMappedType = tsMappedType;
+exports.tSMethodSignature = exports.tsMethodSignature = tsMethodSignature;
+exports.tSModuleBlock = exports.tsModuleBlock = tsModuleBlock;
+exports.tSModuleDeclaration = exports.tsModuleDeclaration = tsModuleDeclaration;
+exports.tSNamedTupleMember = exports.tsNamedTupleMember = tsNamedTupleMember;
+exports.tSNamespaceExportDeclaration = exports.tsNamespaceExportDeclaration = tsNamespaceExportDeclaration;
+exports.tSNeverKeyword = exports.tsNeverKeyword = tsNeverKeyword;
+exports.tSNonNullExpression = exports.tsNonNullExpression = tsNonNullExpression;
+exports.tSNullKeyword = exports.tsNullKeyword = tsNullKeyword;
+exports.tSNumberKeyword = exports.tsNumberKeyword = tsNumberKeyword;
+exports.tSObjectKeyword = exports.tsObjectKeyword = tsObjectKeyword;
+exports.tSOptionalType = exports.tsOptionalType = tsOptionalType;
+exports.tSParameterProperty = exports.tsParameterProperty = tsParameterProperty;
+exports.tSParenthesizedType = exports.tsParenthesizedType = tsParenthesizedType;
+exports.tSPropertySignature = exports.tsPropertySignature = tsPropertySignature;
+exports.tSQualifiedName = exports.tsQualifiedName = tsQualifiedName;
+exports.tSRestType = exports.tsRestType = tsRestType;
+exports.tSSatisfiesExpression = exports.tsSatisfiesExpression = tsSatisfiesExpression;
+exports.tSStringKeyword = exports.tsStringKeyword = tsStringKeyword;
+exports.tSSymbolKeyword = exports.tsSymbolKeyword = tsSymbolKeyword;
+exports.tSTemplateLiteralType = exports.tsTemplateLiteralType = tsTemplateLiteralType;
+exports.tSThisType = exports.tsThisType = tsThisType;
+exports.tSTupleType = exports.tsTupleType = tsTupleType;
+exports.tSTypeAliasDeclaration = exports.tsTypeAliasDeclaration = tsTypeAliasDeclaration;
+exports.tSTypeAnnotation = exports.tsTypeAnnotation = tsTypeAnnotation;
+exports.tSTypeAssertion = exports.tsTypeAssertion = tsTypeAssertion;
+exports.tSTypeLiteral = exports.tsTypeLiteral = tsTypeLiteral;
+exports.tSTypeOperator = exports.tsTypeOperator = tsTypeOperator;
+exports.tSTypeParameter = exports.tsTypeParameter = tsTypeParameter;
+exports.tSTypeParameterDeclaration = exports.tsTypeParameterDeclaration = tsTypeParameterDeclaration;
+exports.tSTypeParameterInstantiation = exports.tsTypeParameterInstantiation = tsTypeParameterInstantiation;
+exports.tSTypePredicate = exports.tsTypePredicate = tsTypePredicate;
+exports.tSTypeQuery = exports.tsTypeQuery = tsTypeQuery;
+exports.tSTypeReference = exports.tsTypeReference = tsTypeReference;
+exports.tSUndefinedKeyword = exports.tsUndefinedKeyword = tsUndefinedKeyword;
+exports.tSUnionType = exports.tsUnionType = tsUnionType;
+exports.tSUnknownKeyword = exports.tsUnknownKeyword = tsUnknownKeyword;
+exports.tSVoidKeyword = exports.tsVoidKeyword = tsVoidKeyword;
+exports.tupleExpression = tupleExpression;
+exports.tupleTypeAnnotation = tupleTypeAnnotation;
+exports.typeAlias = typeAlias;
+exports.typeAnnotation = typeAnnotation;
+exports.typeCastExpression = typeCastExpression;
+exports.typeParameter = typeParameter;
+exports.typeParameterDeclaration = typeParameterDeclaration;
+exports.typeParameterInstantiation = typeParameterInstantiation;
+exports.typeofTypeAnnotation = typeofTypeAnnotation;
+exports.unaryExpression = unaryExpression;
+exports.unionTypeAnnotation = unionTypeAnnotation;
+exports.updateExpression = updateExpression;
+exports.v8IntrinsicIdentifier = v8IntrinsicIdentifier;
+exports.variableDeclaration = variableDeclaration;
+exports.variableDeclarator = variableDeclarator;
+exports.variance = variance;
+exports.voidPattern = voidPattern;
+exports.voidTypeAnnotation = voidTypeAnnotation;
+exports.whileStatement = whileStatement;
+exports.withStatement = withStatement;
+exports.yieldExpression = yieldExpression;
+var _validate = require("../../validators/validate.js");
+var _deprecationWarning = require("../../utils/deprecationWarning.js");
+var utils = require("../../definitions/utils.js");
+const {
+  validateInternal: validate
+} = _validate;
+const {
+  NODE_FIELDS
+} = utils;
+function bigIntLiteral(value) {
+  if (typeof value === "bigint") {
+    value = value.toString();
+  }
+  const node = {
+    type: "BigIntLiteral",
+    value
+  };
+  const defs = NODE_FIELDS.BigIntLiteral;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function arrayExpression(elements = []) {
+  const node = {
+    type: "ArrayExpression",
+    elements
+  };
+  const defs = NODE_FIELDS.ArrayExpression;
+  validate(defs.elements, node, "elements", elements, 1);
+  return node;
+}
+function assignmentExpression(operator, left, right) {
+  const node = {
+    type: "AssignmentExpression",
+    operator,
+    left,
+    right
+  };
+  const defs = NODE_FIELDS.AssignmentExpression;
+  validate(defs.operator, node, "operator", operator);
+  validate(defs.left, node, "left", left, 1);
+  validate(defs.right, node, "right", right, 1);
+  return node;
+}
+function binaryExpression(operator, left, right) {
+  const node = {
+    type: "BinaryExpression",
+    operator,
+    left,
+    right
+  };
+  const defs = NODE_FIELDS.BinaryExpression;
+  validate(defs.operator, node, "operator", operator);
+  validate(defs.left, node, "left", left, 1);
+  validate(defs.right, node, "right", right, 1);
+  return node;
+}
+function interpreterDirective(value) {
+  const node = {
+    type: "InterpreterDirective",
+    value
+  };
+  const defs = NODE_FIELDS.InterpreterDirective;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function directive(value) {
+  const node = {
+    type: "Directive",
+    value
+  };
+  const defs = NODE_FIELDS.Directive;
+  validate(defs.value, node, "value", value, 1);
+  return node;
+}
+function directiveLiteral(value) {
+  const node = {
+    type: "DirectiveLiteral",
+    value
+  };
+  const defs = NODE_FIELDS.DirectiveLiteral;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function blockStatement(body, directives = []) {
+  const node = {
+    type: "BlockStatement",
+    body,
+    directives
+  };
+  const defs = NODE_FIELDS.BlockStatement;
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.directives, node, "directives", directives, 1);
+  return node;
+}
+function breakStatement(label = null) {
+  const node = {
+    type: "BreakStatement",
+    label
+  };
+  const defs = NODE_FIELDS.BreakStatement;
+  validate(defs.label, node, "label", label, 1);
+  return node;
+}
+function callExpression(callee, _arguments) {
+  const node = {
+    type: "CallExpression",
+    callee,
+    arguments: _arguments
+  };
+  const defs = NODE_FIELDS.CallExpression;
+  validate(defs.callee, node, "callee", callee, 1);
+  validate(defs.arguments, node, "arguments", _arguments, 1);
+  return node;
+}
+function catchClause(param = null, body) {
+  const node = {
+    type: "CatchClause",
+    param,
+    body
+  };
+  const defs = NODE_FIELDS.CatchClause;
+  validate(defs.param, node, "param", param, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function conditionalExpression(test, consequent, alternate) {
+  const node = {
+    type: "ConditionalExpression",
+    test,
+    consequent,
+    alternate
+  };
+  const defs = NODE_FIELDS.ConditionalExpression;
+  validate(defs.test, node, "test", test, 1);
+  validate(defs.consequent, node, "consequent", consequent, 1);
+  validate(defs.alternate, node, "alternate", alternate, 1);
+  return node;
+}
+function continueStatement(label = null) {
+  const node = {
+    type: "ContinueStatement",
+    label
+  };
+  const defs = NODE_FIELDS.ContinueStatement;
+  validate(defs.label, node, "label", label, 1);
+  return node;
+}
+function debuggerStatement() {
+  return {
+    type: "DebuggerStatement"
+  };
+}
+function doWhileStatement(test, body) {
+  const node = {
+    type: "DoWhileStatement",
+    test,
+    body
+  };
+  const defs = NODE_FIELDS.DoWhileStatement;
+  validate(defs.test, node, "test", test, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function emptyStatement() {
+  return {
+    type: "EmptyStatement"
+  };
+}
+function expressionStatement(expression) {
+  const node = {
+    type: "ExpressionStatement",
+    expression
+  };
+  const defs = NODE_FIELDS.ExpressionStatement;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function file(program, comments = null, tokens = null) {
+  const node = {
+    type: "File",
+    program,
+    comments,
+    tokens
+  };
+  const defs = NODE_FIELDS.File;
+  validate(defs.program, node, "program", program, 1);
+  validate(defs.comments, node, "comments", comments, 1);
+  validate(defs.tokens, node, "tokens", tokens);
+  return node;
+}
+function forInStatement(left, right, body) {
+  const node = {
+    type: "ForInStatement",
+    left,
+    right,
+    body
+  };
+  const defs = NODE_FIELDS.ForInStatement;
+  validate(defs.left, node, "left", left, 1);
+  validate(defs.right, node, "right", right, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function forStatement(init = null, test = null, update = null, body) {
+  const node = {
+    type: "ForStatement",
+    init,
+    test,
+    update,
+    body
+  };
+  const defs = NODE_FIELDS.ForStatement;
+  validate(defs.init, node, "init", init, 1);
+  validate(defs.test, node, "test", test, 1);
+  validate(defs.update, node, "update", update, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function functionDeclaration(id = null, params, body, generator = false, async = false) {
+  const node = {
+    type: "FunctionDeclaration",
+    id,
+    params,
+    body,
+    generator,
+    async
+  };
+  const defs = NODE_FIELDS.FunctionDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.generator, node, "generator", generator);
+  validate(defs.async, node, "async", async);
+  return node;
+}
+function functionExpression(id = null, params, body, generator = false, async = false) {
+  const node = {
+    type: "FunctionExpression",
+    id,
+    params,
+    body,
+    generator,
+    async
+  };
+  const defs = NODE_FIELDS.FunctionExpression;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.generator, node, "generator", generator);
+  validate(defs.async, node, "async", async);
+  return node;
+}
+function identifier(name) {
+  const node = {
+    type: "Identifier",
+    name
+  };
+  const defs = NODE_FIELDS.Identifier;
+  validate(defs.name, node, "name", name);
+  return node;
+}
+function ifStatement(test, consequent, alternate = null) {
+  const node = {
+    type: "IfStatement",
+    test,
+    consequent,
+    alternate
+  };
+  const defs = NODE_FIELDS.IfStatement;
+  validate(defs.test, node, "test", test, 1);
+  validate(defs.consequent, node, "consequent", consequent, 1);
+  validate(defs.alternate, node, "alternate", alternate, 1);
+  return node;
+}
+function labeledStatement(label, body) {
+  const node = {
+    type: "LabeledStatement",
+    label,
+    body
+  };
+  const defs = NODE_FIELDS.LabeledStatement;
+  validate(defs.label, node, "label", label, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function stringLiteral(value) {
+  const node = {
+    type: "StringLiteral",
+    value
+  };
+  const defs = NODE_FIELDS.StringLiteral;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function numericLiteral(value) {
+  const node = {
+    type: "NumericLiteral",
+    value
+  };
+  const defs = NODE_FIELDS.NumericLiteral;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function nullLiteral() {
+  return {
+    type: "NullLiteral"
+  };
+}
+function booleanLiteral(value) {
+  const node = {
+    type: "BooleanLiteral",
+    value
+  };
+  const defs = NODE_FIELDS.BooleanLiteral;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function regExpLiteral(pattern, flags = "") {
+  const node = {
+    type: "RegExpLiteral",
+    pattern,
+    flags
+  };
+  const defs = NODE_FIELDS.RegExpLiteral;
+  validate(defs.pattern, node, "pattern", pattern);
+  validate(defs.flags, node, "flags", flags);
+  return node;
+}
+function logicalExpression(operator, left, right) {
+  const node = {
+    type: "LogicalExpression",
+    operator,
+    left,
+    right
+  };
+  const defs = NODE_FIELDS.LogicalExpression;
+  validate(defs.operator, node, "operator", operator);
+  validate(defs.left, node, "left", left, 1);
+  validate(defs.right, node, "right", right, 1);
+  return node;
+}
+function memberExpression(object, property, computed = false, optional = null) {
+  const node = {
+    type: "MemberExpression",
+    object,
+    property,
+    computed,
+    optional
+  };
+  const defs = NODE_FIELDS.MemberExpression;
+  validate(defs.object, node, "object", object, 1);
+  validate(defs.property, node, "property", property, 1);
+  validate(defs.computed, node, "computed", computed);
+  validate(defs.optional, node, "optional", optional);
+  return node;
+}
+function newExpression(callee, _arguments) {
+  const node = {
+    type: "NewExpression",
+    callee,
+    arguments: _arguments,
+    typeParameters: null
+  };
+  const defs = NODE_FIELDS.NewExpression;
+  validate(defs.callee, node, "callee", callee, 1);
+  validate(defs.arguments, node, "arguments", _arguments, 1);
+  return node;
+}
+function program(body, directives = [], sourceType = "script", interpreter = null) {
+  const node = {
+    type: "Program",
+    body,
+    directives,
+    sourceType,
+    interpreter
+  };
+  const defs = NODE_FIELDS.Program;
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.directives, node, "directives", directives, 1);
+  validate(defs.sourceType, node, "sourceType", sourceType);
+  validate(defs.interpreter, node, "interpreter", interpreter, 1);
+  return node;
+}
+function objectExpression(properties) {
+  const node = {
+    type: "ObjectExpression",
+    properties
+  };
+  const defs = NODE_FIELDS.ObjectExpression;
+  validate(defs.properties, node, "properties", properties, 1);
+  return node;
+}
+function objectMethod(kind = "method", key, params, body, computed = false, generator = false, async = false) {
+  const node = {
+    type: "ObjectMethod",
+    kind,
+    key,
+    params,
+    body,
+    computed,
+    generator,
+    async
+  };
+  const defs = NODE_FIELDS.ObjectMethod;
+  validate(defs.kind, node, "kind", kind);
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.computed, node, "computed", computed);
+  validate(defs.generator, node, "generator", generator);
+  validate(defs.async, node, "async", async);
+  return node;
+}
+function objectProperty(key, value, computed = false, shorthand = false, decorators = null) {
+  const node = {
+    type: "ObjectProperty",
+    key,
+    value,
+    computed,
+    shorthand,
+    decorators
+  };
+  const defs = NODE_FIELDS.ObjectProperty;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.value, node, "value", value, 1);
+  validate(defs.computed, node, "computed", computed);
+  validate(defs.shorthand, node, "shorthand", shorthand);
+  validate(defs.decorators, node, "decorators", decorators, 1);
+  return node;
+}
+function restElement(argument) {
+  const node = {
+    type: "RestElement",
+    argument
+  };
+  const defs = NODE_FIELDS.RestElement;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function returnStatement(argument = null) {
+  const node = {
+    type: "ReturnStatement",
+    argument
+  };
+  const defs = NODE_FIELDS.ReturnStatement;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function sequenceExpression(expressions) {
+  const node = {
+    type: "SequenceExpression",
+    expressions
+  };
+  const defs = NODE_FIELDS.SequenceExpression;
+  validate(defs.expressions, node, "expressions", expressions, 1);
+  return node;
+}
+function parenthesizedExpression(expression) {
+  const node = {
+    type: "ParenthesizedExpression",
+    expression
+  };
+  const defs = NODE_FIELDS.ParenthesizedExpression;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function switchCase(test = null, consequent) {
+  const node = {
+    type: "SwitchCase",
+    test,
+    consequent
+  };
+  const defs = NODE_FIELDS.SwitchCase;
+  validate(defs.test, node, "test", test, 1);
+  validate(defs.consequent, node, "consequent", consequent, 1);
+  return node;
+}
+function switchStatement(discriminant, cases) {
+  const node = {
+    type: "SwitchStatement",
+    discriminant,
+    cases
+  };
+  const defs = NODE_FIELDS.SwitchStatement;
+  validate(defs.discriminant, node, "discriminant", discriminant, 1);
+  validate(defs.cases, node, "cases", cases, 1);
+  return node;
+}
+function thisExpression() {
+  return {
+    type: "ThisExpression"
+  };
+}
+function throwStatement(argument) {
+  const node = {
+    type: "ThrowStatement",
+    argument
+  };
+  const defs = NODE_FIELDS.ThrowStatement;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function tryStatement(block, handler = null, finalizer = null) {
+  const node = {
+    type: "TryStatement",
+    block,
+    handler,
+    finalizer
+  };
+  const defs = NODE_FIELDS.TryStatement;
+  validate(defs.block, node, "block", block, 1);
+  validate(defs.handler, node, "handler", handler, 1);
+  validate(defs.finalizer, node, "finalizer", finalizer, 1);
+  return node;
+}
+function unaryExpression(operator, argument, prefix = true) {
+  const node = {
+    type: "UnaryExpression",
+    operator,
+    argument,
+    prefix
+  };
+  const defs = NODE_FIELDS.UnaryExpression;
+  validate(defs.operator, node, "operator", operator);
+  validate(defs.argument, node, "argument", argument, 1);
+  validate(defs.prefix, node, "prefix", prefix);
+  return node;
+}
+function updateExpression(operator, argument, prefix = false) {
+  const node = {
+    type: "UpdateExpression",
+    operator,
+    argument,
+    prefix
+  };
+  const defs = NODE_FIELDS.UpdateExpression;
+  validate(defs.operator, node, "operator", operator);
+  validate(defs.argument, node, "argument", argument, 1);
+  validate(defs.prefix, node, "prefix", prefix);
+  return node;
+}
+function variableDeclaration(kind, declarations) {
+  const node = {
+    type: "VariableDeclaration",
+    kind,
+    declarations
+  };
+  const defs = NODE_FIELDS.VariableDeclaration;
+  validate(defs.kind, node, "kind", kind);
+  validate(defs.declarations, node, "declarations", declarations, 1);
+  return node;
+}
+function variableDeclarator(id, init = null) {
+  const node = {
+    type: "VariableDeclarator",
+    id,
+    init
+  };
+  const defs = NODE_FIELDS.VariableDeclarator;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.init, node, "init", init, 1);
+  return node;
+}
+function whileStatement(test, body) {
+  const node = {
+    type: "WhileStatement",
+    test,
+    body
+  };
+  const defs = NODE_FIELDS.WhileStatement;
+  validate(defs.test, node, "test", test, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function withStatement(object, body) {
+  const node = {
+    type: "WithStatement",
+    object,
+    body
+  };
+  const defs = NODE_FIELDS.WithStatement;
+  validate(defs.object, node, "object", object, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function assignmentPattern(left, right) {
+  const node = {
+    type: "AssignmentPattern",
+    left,
+    right
+  };
+  const defs = NODE_FIELDS.AssignmentPattern;
+  validate(defs.left, node, "left", left, 1);
+  validate(defs.right, node, "right", right, 1);
+  return node;
+}
+function arrayPattern(elements) {
+  const node = {
+    type: "ArrayPattern",
+    elements
+  };
+  const defs = NODE_FIELDS.ArrayPattern;
+  validate(defs.elements, node, "elements", elements, 1);
+  return node;
+}
+function arrowFunctionExpression(params, body, async = false) {
+  const node = {
+    type: "ArrowFunctionExpression",
+    params,
+    body,
+    async,
+    expression: null
+  };
+  const defs = NODE_FIELDS.ArrowFunctionExpression;
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.async, node, "async", async);
+  return node;
+}
+function classBody(body) {
+  const node = {
+    type: "ClassBody",
+    body
+  };
+  const defs = NODE_FIELDS.ClassBody;
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function classExpression(id = null, superClass = null, body, decorators = null) {
+  const node = {
+    type: "ClassExpression",
+    id,
+    superClass,
+    body,
+    decorators
+  };
+  const defs = NODE_FIELDS.ClassExpression;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.superClass, node, "superClass", superClass, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.decorators, node, "decorators", decorators, 1);
+  return node;
+}
+function classDeclaration(id = null, superClass = null, body, decorators = null) {
+  const node = {
+    type: "ClassDeclaration",
+    id,
+    superClass,
+    body,
+    decorators
+  };
+  const defs = NODE_FIELDS.ClassDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.superClass, node, "superClass", superClass, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.decorators, node, "decorators", decorators, 1);
+  return node;
+}
+function exportAllDeclaration(source, attributes = null) {
+  const node = {
+    type: "ExportAllDeclaration",
+    source,
+    attributes
+  };
+  const defs = NODE_FIELDS.ExportAllDeclaration;
+  validate(defs.source, node, "source", source, 1);
+  validate(defs.attributes, node, "attributes", attributes, 1);
+  return node;
+}
+function exportDefaultDeclaration(declaration) {
+  const node = {
+    type: "ExportDefaultDeclaration",
+    declaration
+  };
+  const defs = NODE_FIELDS.ExportDefaultDeclaration;
+  validate(defs.declaration, node, "declaration", declaration, 1);
+  return node;
+}
+function exportNamedDeclaration(declaration = null, specifiers = [], source = null, attributes = null) {
+  const node = {
+    type: "ExportNamedDeclaration",
+    declaration,
+    specifiers,
+    source,
+    attributes
+  };
+  const defs = NODE_FIELDS.ExportNamedDeclaration;
+  validate(defs.declaration, node, "declaration", declaration, 1);
+  validate(defs.specifiers, node, "specifiers", specifiers, 1);
+  validate(defs.source, node, "source", source, 1);
+  validate(defs.attributes, node, "attributes", attributes, 1);
+  return node;
+}
+function exportSpecifier(local, exported) {
+  const node = {
+    type: "ExportSpecifier",
+    local,
+    exported
+  };
+  const defs = NODE_FIELDS.ExportSpecifier;
+  validate(defs.local, node, "local", local, 1);
+  validate(defs.exported, node, "exported", exported, 1);
+  return node;
+}
+function forOfStatement(left, right, body, _await = false) {
+  const node = {
+    type: "ForOfStatement",
+    left,
+    right,
+    body,
+    await: _await
+  };
+  const defs = NODE_FIELDS.ForOfStatement;
+  validate(defs.left, node, "left", left, 1);
+  validate(defs.right, node, "right", right, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.await, node, "await", _await);
+  return node;
+}
+function importDeclaration(specifiers, source, attributes = null) {
+  const node = {
+    type: "ImportDeclaration",
+    specifiers,
+    source,
+    attributes
+  };
+  const defs = NODE_FIELDS.ImportDeclaration;
+  validate(defs.specifiers, node, "specifiers", specifiers, 1);
+  validate(defs.source, node, "source", source, 1);
+  validate(defs.attributes, node, "attributes", attributes, 1);
+  return node;
+}
+function importDefaultSpecifier(local) {
+  const node = {
+    type: "ImportDefaultSpecifier",
+    local
+  };
+  const defs = NODE_FIELDS.ImportDefaultSpecifier;
+  validate(defs.local, node, "local", local, 1);
+  return node;
+}
+function importNamespaceSpecifier(local) {
+  const node = {
+    type: "ImportNamespaceSpecifier",
+    local
+  };
+  const defs = NODE_FIELDS.ImportNamespaceSpecifier;
+  validate(defs.local, node, "local", local, 1);
+  return node;
+}
+function importSpecifier(local, imported) {
+  const node = {
+    type: "ImportSpecifier",
+    local,
+    imported
+  };
+  const defs = NODE_FIELDS.ImportSpecifier;
+  validate(defs.local, node, "local", local, 1);
+  validate(defs.imported, node, "imported", imported, 1);
+  return node;
+}
+function importExpression(source, options = null) {
+  const node = {
+    type: "ImportExpression",
+    source,
+    options
+  };
+  const defs = NODE_FIELDS.ImportExpression;
+  validate(defs.source, node, "source", source, 1);
+  validate(defs.options, node, "options", options, 1);
+  return node;
+}
+function metaProperty(meta, property) {
+  const node = {
+    type: "MetaProperty",
+    meta,
+    property
+  };
+  const defs = NODE_FIELDS.MetaProperty;
+  validate(defs.meta, node, "meta", meta, 1);
+  validate(defs.property, node, "property", property, 1);
+  return node;
+}
+function classMethod(kind = "method", key, params, body, computed = false, _static = false, generator = false, async = false) {
+  const node = {
+    type: "ClassMethod",
+    kind,
+    key,
+    params,
+    body,
+    computed,
+    static: _static,
+    generator,
+    async
+  };
+  const defs = NODE_FIELDS.ClassMethod;
+  validate(defs.kind, node, "kind", kind);
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.computed, node, "computed", computed);
+  validate(defs.static, node, "static", _static);
+  validate(defs.generator, node, "generator", generator);
+  validate(defs.async, node, "async", async);
+  return node;
+}
+function objectPattern(properties) {
+  const node = {
+    type: "ObjectPattern",
+    properties
+  };
+  const defs = NODE_FIELDS.ObjectPattern;
+  validate(defs.properties, node, "properties", properties, 1);
+  return node;
+}
+function spreadElement(argument) {
+  const node = {
+    type: "SpreadElement",
+    argument
+  };
+  const defs = NODE_FIELDS.SpreadElement;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function _super() {
+  return {
+    type: "Super"
+  };
+}
+function taggedTemplateExpression(tag, quasi) {
+  const node = {
+    type: "TaggedTemplateExpression",
+    tag,
+    quasi
+  };
+  const defs = NODE_FIELDS.TaggedTemplateExpression;
+  validate(defs.tag, node, "tag", tag, 1);
+  validate(defs.quasi, node, "quasi", quasi, 1);
+  return node;
+}
+function templateElement(value, tail = false) {
+  const node = {
+    type: "TemplateElement",
+    value,
+    tail
+  };
+  const defs = NODE_FIELDS.TemplateElement;
+  validate(defs.value, node, "value", value);
+  validate(defs.tail, node, "tail", tail);
+  return node;
+}
+function templateLiteral(quasis, expressions) {
+  const node = {
+    type: "TemplateLiteral",
+    quasis,
+    expressions
+  };
+  const defs = NODE_FIELDS.TemplateLiteral;
+  validate(defs.quasis, node, "quasis", quasis, 1);
+  validate(defs.expressions, node, "expressions", expressions, 1);
+  return node;
+}
+function yieldExpression(argument = null, delegate = false) {
+  const node = {
+    type: "YieldExpression",
+    argument,
+    delegate
+  };
+  const defs = NODE_FIELDS.YieldExpression;
+  validate(defs.argument, node, "argument", argument, 1);
+  validate(defs.delegate, node, "delegate", delegate);
+  return node;
+}
+function awaitExpression(argument) {
+  const node = {
+    type: "AwaitExpression",
+    argument
+  };
+  const defs = NODE_FIELDS.AwaitExpression;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function _import() {
+  return {
+    type: "Import"
+  };
+}
+function exportNamespaceSpecifier(exported) {
+  const node = {
+    type: "ExportNamespaceSpecifier",
+    exported
+  };
+  const defs = NODE_FIELDS.ExportNamespaceSpecifier;
+  validate(defs.exported, node, "exported", exported, 1);
+  return node;
+}
+function optionalMemberExpression(object, property, computed = false, optional) {
+  const node = {
+    type: "OptionalMemberExpression",
+    object,
+    property,
+    computed,
+    optional
+  };
+  const defs = NODE_FIELDS.OptionalMemberExpression;
+  validate(defs.object, node, "object", object, 1);
+  validate(defs.property, node, "property", property, 1);
+  validate(defs.computed, node, "computed", computed);
+  validate(defs.optional, node, "optional", optional);
+  return node;
+}
+function optionalCallExpression(callee, _arguments, optional) {
+  const node = {
+    type: "OptionalCallExpression",
+    callee,
+    arguments: _arguments,
+    optional
+  };
+  const defs = NODE_FIELDS.OptionalCallExpression;
+  validate(defs.callee, node, "callee", callee, 1);
+  validate(defs.arguments, node, "arguments", _arguments, 1);
+  validate(defs.optional, node, "optional", optional);
+  return node;
+}
+function classProperty(key, value = null, typeAnnotation = null, decorators = null, computed = false, _static = false) {
+  const node = {
+    type: "ClassProperty",
+    key,
+    value,
+    typeAnnotation,
+    decorators,
+    computed,
+    static: _static
+  };
+  const defs = NODE_FIELDS.ClassProperty;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.value, node, "value", value, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  validate(defs.decorators, node, "decorators", decorators, 1);
+  validate(defs.computed, node, "computed", computed);
+  validate(defs.static, node, "static", _static);
+  return node;
+}
+function classAccessorProperty(key, value = null, typeAnnotation = null, decorators = null, computed = false, _static = false) {
+  const node = {
+    type: "ClassAccessorProperty",
+    key,
+    value,
+    typeAnnotation,
+    decorators,
+    computed,
+    static: _static
+  };
+  const defs = NODE_FIELDS.ClassAccessorProperty;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.value, node, "value", value, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  validate(defs.decorators, node, "decorators", decorators, 1);
+  validate(defs.computed, node, "computed", computed);
+  validate(defs.static, node, "static", _static);
+  return node;
+}
+function classPrivateProperty(key, value = null, decorators = null, _static = false) {
+  const node = {
+    type: "ClassPrivateProperty",
+    key,
+    value,
+    decorators,
+    static: _static
+  };
+  const defs = NODE_FIELDS.ClassPrivateProperty;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.value, node, "value", value, 1);
+  validate(defs.decorators, node, "decorators", decorators, 1);
+  validate(defs.static, node, "static", _static);
+  return node;
+}
+function classPrivateMethod(kind = "method", key, params, body, _static = false) {
+  const node = {
+    type: "ClassPrivateMethod",
+    kind,
+    key,
+    params,
+    body,
+    static: _static
+  };
+  const defs = NODE_FIELDS.ClassPrivateMethod;
+  validate(defs.kind, node, "kind", kind);
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.static, node, "static", _static);
+  return node;
+}
+function privateName(id) {
+  const node = {
+    type: "PrivateName",
+    id
+  };
+  const defs = NODE_FIELDS.PrivateName;
+  validate(defs.id, node, "id", id, 1);
+  return node;
+}
+function staticBlock(body) {
+  const node = {
+    type: "StaticBlock",
+    body
+  };
+  const defs = NODE_FIELDS.StaticBlock;
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function importAttribute(key, value) {
+  const node = {
+    type: "ImportAttribute",
+    key,
+    value
+  };
+  const defs = NODE_FIELDS.ImportAttribute;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.value, node, "value", value, 1);
+  return node;
+}
+function anyTypeAnnotation() {
+  return {
+    type: "AnyTypeAnnotation"
+  };
+}
+function arrayTypeAnnotation(elementType) {
+  const node = {
+    type: "ArrayTypeAnnotation",
+    elementType
+  };
+  const defs = NODE_FIELDS.ArrayTypeAnnotation;
+  validate(defs.elementType, node, "elementType", elementType, 1);
+  return node;
+}
+function booleanTypeAnnotation() {
+  return {
+    type: "BooleanTypeAnnotation"
+  };
+}
+function booleanLiteralTypeAnnotation(value) {
+  const node = {
+    type: "BooleanLiteralTypeAnnotation",
+    value
+  };
+  const defs = NODE_FIELDS.BooleanLiteralTypeAnnotation;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function nullLiteralTypeAnnotation() {
+  return {
+    type: "NullLiteralTypeAnnotation"
+  };
+}
+function classImplements(id, typeParameters = null) {
+  const node = {
+    type: "ClassImplements",
+    id,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.ClassImplements;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function declareClass(id, typeParameters = null, _extends = null, body) {
+  const node = {
+    type: "DeclareClass",
+    id,
+    typeParameters,
+    extends: _extends,
+    body
+  };
+  const defs = NODE_FIELDS.DeclareClass;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.extends, node, "extends", _extends, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function declareFunction(id) {
+  const node = {
+    type: "DeclareFunction",
+    id
+  };
+  const defs = NODE_FIELDS.DeclareFunction;
+  validate(defs.id, node, "id", id, 1);
+  return node;
+}
+function declareInterface(id, typeParameters = null, _extends = null, body) {
+  const node = {
+    type: "DeclareInterface",
+    id,
+    typeParameters,
+    extends: _extends,
+    body
+  };
+  const defs = NODE_FIELDS.DeclareInterface;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.extends, node, "extends", _extends, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function declareModule(id, body, kind = null) {
+  const node = {
+    type: "DeclareModule",
+    id,
+    body,
+    kind
+  };
+  const defs = NODE_FIELDS.DeclareModule;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.kind, node, "kind", kind);
+  return node;
+}
+function declareModuleExports(typeAnnotation) {
+  const node = {
+    type: "DeclareModuleExports",
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.DeclareModuleExports;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function declareTypeAlias(id, typeParameters = null, right) {
+  const node = {
+    type: "DeclareTypeAlias",
+    id,
+    typeParameters,
+    right
+  };
+  const defs = NODE_FIELDS.DeclareTypeAlias;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.right, node, "right", right, 1);
+  return node;
+}
+function declareOpaqueType(id, typeParameters = null, supertype = null) {
+  const node = {
+    type: "DeclareOpaqueType",
+    id,
+    typeParameters,
+    supertype
+  };
+  const defs = NODE_FIELDS.DeclareOpaqueType;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.supertype, node, "supertype", supertype, 1);
+  return node;
+}
+function declareVariable(id) {
+  const node = {
+    type: "DeclareVariable",
+    id
+  };
+  const defs = NODE_FIELDS.DeclareVariable;
+  validate(defs.id, node, "id", id, 1);
+  return node;
+}
+function declareExportDeclaration(declaration = null, specifiers = null, source = null, attributes = null) {
+  const node = {
+    type: "DeclareExportDeclaration",
+    declaration,
+    specifiers,
+    source,
+    attributes
+  };
+  const defs = NODE_FIELDS.DeclareExportDeclaration;
+  validate(defs.declaration, node, "declaration", declaration, 1);
+  validate(defs.specifiers, node, "specifiers", specifiers, 1);
+  validate(defs.source, node, "source", source, 1);
+  validate(defs.attributes, node, "attributes", attributes, 1);
+  return node;
+}
+function declareExportAllDeclaration(source, attributes = null) {
+  const node = {
+    type: "DeclareExportAllDeclaration",
+    source,
+    attributes
+  };
+  const defs = NODE_FIELDS.DeclareExportAllDeclaration;
+  validate(defs.source, node, "source", source, 1);
+  validate(defs.attributes, node, "attributes", attributes, 1);
+  return node;
+}
+function declaredPredicate(value) {
+  const node = {
+    type: "DeclaredPredicate",
+    value
+  };
+  const defs = NODE_FIELDS.DeclaredPredicate;
+  validate(defs.value, node, "value", value, 1);
+  return node;
+}
+function existsTypeAnnotation() {
+  return {
+    type: "ExistsTypeAnnotation"
+  };
+}
+function functionTypeAnnotation(typeParameters = null, params, rest = null, returnType) {
+  const node = {
+    type: "FunctionTypeAnnotation",
+    typeParameters,
+    params,
+    rest,
+    returnType
+  };
+  const defs = NODE_FIELDS.FunctionTypeAnnotation;
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.rest, node, "rest", rest, 1);
+  validate(defs.returnType, node, "returnType", returnType, 1);
+  return node;
+}
+function functionTypeParam(name = null, typeAnnotation) {
+  const node = {
+    type: "FunctionTypeParam",
+    name,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.FunctionTypeParam;
+  validate(defs.name, node, "name", name, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function genericTypeAnnotation(id, typeParameters = null) {
+  const node = {
+    type: "GenericTypeAnnotation",
+    id,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.GenericTypeAnnotation;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function inferredPredicate() {
+  return {
+    type: "InferredPredicate"
+  };
+}
+function interfaceExtends(id, typeParameters = null) {
+  const node = {
+    type: "InterfaceExtends",
+    id,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.InterfaceExtends;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function interfaceDeclaration(id, typeParameters = null, _extends = null, body) {
+  const node = {
+    type: "InterfaceDeclaration",
+    id,
+    typeParameters,
+    extends: _extends,
+    body
+  };
+  const defs = NODE_FIELDS.InterfaceDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.extends, node, "extends", _extends, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function interfaceTypeAnnotation(_extends = null, body) {
+  const node = {
+    type: "InterfaceTypeAnnotation",
+    extends: _extends,
+    body
+  };
+  const defs = NODE_FIELDS.InterfaceTypeAnnotation;
+  validate(defs.extends, node, "extends", _extends, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function intersectionTypeAnnotation(types) {
+  const node = {
+    type: "IntersectionTypeAnnotation",
+    types
+  };
+  const defs = NODE_FIELDS.IntersectionTypeAnnotation;
+  validate(defs.types, node, "types", types, 1);
+  return node;
+}
+function mixedTypeAnnotation() {
+  return {
+    type: "MixedTypeAnnotation"
+  };
+}
+function emptyTypeAnnotation() {
+  return {
+    type: "EmptyTypeAnnotation"
+  };
+}
+function nullableTypeAnnotation(typeAnnotation) {
+  const node = {
+    type: "NullableTypeAnnotation",
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.NullableTypeAnnotation;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function numberLiteralTypeAnnotation(value) {
+  const node = {
+    type: "NumberLiteralTypeAnnotation",
+    value
+  };
+  const defs = NODE_FIELDS.NumberLiteralTypeAnnotation;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function numberTypeAnnotation() {
+  return {
+    type: "NumberTypeAnnotation"
+  };
+}
+function objectTypeAnnotation(properties, indexers = [], callProperties = [], internalSlots = [], exact = false) {
+  const node = {
+    type: "ObjectTypeAnnotation",
+    properties,
+    indexers,
+    callProperties,
+    internalSlots,
+    exact
+  };
+  const defs = NODE_FIELDS.ObjectTypeAnnotation;
+  validate(defs.properties, node, "properties", properties, 1);
+  validate(defs.indexers, node, "indexers", indexers, 1);
+  validate(defs.callProperties, node, "callProperties", callProperties, 1);
+  validate(defs.internalSlots, node, "internalSlots", internalSlots, 1);
+  validate(defs.exact, node, "exact", exact);
+  return node;
+}
+function objectTypeInternalSlot(id, value, optional, _static, method) {
+  const node = {
+    type: "ObjectTypeInternalSlot",
+    id,
+    value,
+    optional,
+    static: _static,
+    method
+  };
+  const defs = NODE_FIELDS.ObjectTypeInternalSlot;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.value, node, "value", value, 1);
+  validate(defs.optional, node, "optional", optional);
+  validate(defs.static, node, "static", _static);
+  validate(defs.method, node, "method", method);
+  return node;
+}
+function objectTypeCallProperty(value) {
+  const node = {
+    type: "ObjectTypeCallProperty",
+    value,
+    static: null
+  };
+  const defs = NODE_FIELDS.ObjectTypeCallProperty;
+  validate(defs.value, node, "value", value, 1);
+  return node;
+}
+function objectTypeIndexer(id = null, key, value, variance = null) {
+  const node = {
+    type: "ObjectTypeIndexer",
+    id,
+    key,
+    value,
+    variance,
+    static: null
+  };
+  const defs = NODE_FIELDS.ObjectTypeIndexer;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.value, node, "value", value, 1);
+  validate(defs.variance, node, "variance", variance, 1);
+  return node;
+}
+function objectTypeProperty(key, value, variance = null) {
+  const node = {
+    type: "ObjectTypeProperty",
+    key,
+    value,
+    variance,
+    kind: null,
+    method: null,
+    optional: null,
+    proto: null,
+    static: null
+  };
+  const defs = NODE_FIELDS.ObjectTypeProperty;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.value, node, "value", value, 1);
+  validate(defs.variance, node, "variance", variance, 1);
+  return node;
+}
+function objectTypeSpreadProperty(argument) {
+  const node = {
+    type: "ObjectTypeSpreadProperty",
+    argument
+  };
+  const defs = NODE_FIELDS.ObjectTypeSpreadProperty;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function opaqueType(id, typeParameters = null, supertype = null, impltype) {
+  const node = {
+    type: "OpaqueType",
+    id,
+    typeParameters,
+    supertype,
+    impltype
+  };
+  const defs = NODE_FIELDS.OpaqueType;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.supertype, node, "supertype", supertype, 1);
+  validate(defs.impltype, node, "impltype", impltype, 1);
+  return node;
+}
+function qualifiedTypeIdentifier(id, qualification) {
+  const node = {
+    type: "QualifiedTypeIdentifier",
+    id,
+    qualification
+  };
+  const defs = NODE_FIELDS.QualifiedTypeIdentifier;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.qualification, node, "qualification", qualification, 1);
+  return node;
+}
+function stringLiteralTypeAnnotation(value) {
+  const node = {
+    type: "StringLiteralTypeAnnotation",
+    value
+  };
+  const defs = NODE_FIELDS.StringLiteralTypeAnnotation;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function stringTypeAnnotation() {
+  return {
+    type: "StringTypeAnnotation"
+  };
+}
+function symbolTypeAnnotation() {
+  return {
+    type: "SymbolTypeAnnotation"
+  };
+}
+function thisTypeAnnotation() {
+  return {
+    type: "ThisTypeAnnotation"
+  };
+}
+function tupleTypeAnnotation(types) {
+  const node = {
+    type: "TupleTypeAnnotation",
+    types
+  };
+  const defs = NODE_FIELDS.TupleTypeAnnotation;
+  validate(defs.types, node, "types", types, 1);
+  return node;
+}
+function typeofTypeAnnotation(argument) {
+  const node = {
+    type: "TypeofTypeAnnotation",
+    argument
+  };
+  const defs = NODE_FIELDS.TypeofTypeAnnotation;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function typeAlias(id, typeParameters = null, right) {
+  const node = {
+    type: "TypeAlias",
+    id,
+    typeParameters,
+    right
+  };
+  const defs = NODE_FIELDS.TypeAlias;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.right, node, "right", right, 1);
+  return node;
+}
+function typeAnnotation(typeAnnotation) {
+  const node = {
+    type: "TypeAnnotation",
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TypeAnnotation;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function typeCastExpression(expression, typeAnnotation) {
+  const node = {
+    type: "TypeCastExpression",
+    expression,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TypeCastExpression;
+  validate(defs.expression, node, "expression", expression, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function typeParameter(bound = null, _default = null, variance = null) {
+  const node = {
+    type: "TypeParameter",
+    bound,
+    default: _default,
+    variance,
+    name: null
+  };
+  const defs = NODE_FIELDS.TypeParameter;
+  validate(defs.bound, node, "bound", bound, 1);
+  validate(defs.default, node, "default", _default, 1);
+  validate(defs.variance, node, "variance", variance, 1);
+  return node;
+}
+function typeParameterDeclaration(params) {
+  const node = {
+    type: "TypeParameterDeclaration",
+    params
+  };
+  const defs = NODE_FIELDS.TypeParameterDeclaration;
+  validate(defs.params, node, "params", params, 1);
+  return node;
+}
+function typeParameterInstantiation(params) {
+  const node = {
+    type: "TypeParameterInstantiation",
+    params
+  };
+  const defs = NODE_FIELDS.TypeParameterInstantiation;
+  validate(defs.params, node, "params", params, 1);
+  return node;
+}
+function unionTypeAnnotation(types) {
+  const node = {
+    type: "UnionTypeAnnotation",
+    types
+  };
+  const defs = NODE_FIELDS.UnionTypeAnnotation;
+  validate(defs.types, node, "types", types, 1);
+  return node;
+}
+function variance(kind) {
+  const node = {
+    type: "Variance",
+    kind
+  };
+  const defs = NODE_FIELDS.Variance;
+  validate(defs.kind, node, "kind", kind);
+  return node;
+}
+function voidTypeAnnotation() {
+  return {
+    type: "VoidTypeAnnotation"
+  };
+}
+function enumDeclaration(id, body) {
+  const node = {
+    type: "EnumDeclaration",
+    id,
+    body
+  };
+  const defs = NODE_FIELDS.EnumDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function enumBooleanBody(members) {
+  const node = {
+    type: "EnumBooleanBody",
+    members,
+    explicitType: null,
+    hasUnknownMembers: null
+  };
+  const defs = NODE_FIELDS.EnumBooleanBody;
+  validate(defs.members, node, "members", members, 1);
+  return node;
+}
+function enumNumberBody(members) {
+  const node = {
+    type: "EnumNumberBody",
+    members,
+    explicitType: null,
+    hasUnknownMembers: null
+  };
+  const defs = NODE_FIELDS.EnumNumberBody;
+  validate(defs.members, node, "members", members, 1);
+  return node;
+}
+function enumStringBody(members) {
+  const node = {
+    type: "EnumStringBody",
+    members,
+    explicitType: null,
+    hasUnknownMembers: null
+  };
+  const defs = NODE_FIELDS.EnumStringBody;
+  validate(defs.members, node, "members", members, 1);
+  return node;
+}
+function enumSymbolBody(members) {
+  const node = {
+    type: "EnumSymbolBody",
+    members,
+    hasUnknownMembers: null
+  };
+  const defs = NODE_FIELDS.EnumSymbolBody;
+  validate(defs.members, node, "members", members, 1);
+  return node;
+}
+function enumBooleanMember(id) {
+  const node = {
+    type: "EnumBooleanMember",
+    id,
+    init: null
+  };
+  const defs = NODE_FIELDS.EnumBooleanMember;
+  validate(defs.id, node, "id", id, 1);
+  return node;
+}
+function enumNumberMember(id, init) {
+  const node = {
+    type: "EnumNumberMember",
+    id,
+    init
+  };
+  const defs = NODE_FIELDS.EnumNumberMember;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.init, node, "init", init, 1);
+  return node;
+}
+function enumStringMember(id, init) {
+  const node = {
+    type: "EnumStringMember",
+    id,
+    init
+  };
+  const defs = NODE_FIELDS.EnumStringMember;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.init, node, "init", init, 1);
+  return node;
+}
+function enumDefaultedMember(id) {
+  const node = {
+    type: "EnumDefaultedMember",
+    id
+  };
+  const defs = NODE_FIELDS.EnumDefaultedMember;
+  validate(defs.id, node, "id", id, 1);
+  return node;
+}
+function indexedAccessType(objectType, indexType) {
+  const node = {
+    type: "IndexedAccessType",
+    objectType,
+    indexType
+  };
+  const defs = NODE_FIELDS.IndexedAccessType;
+  validate(defs.objectType, node, "objectType", objectType, 1);
+  validate(defs.indexType, node, "indexType", indexType, 1);
+  return node;
+}
+function optionalIndexedAccessType(objectType, indexType) {
+  const node = {
+    type: "OptionalIndexedAccessType",
+    objectType,
+    indexType,
+    optional: null
+  };
+  const defs = NODE_FIELDS.OptionalIndexedAccessType;
+  validate(defs.objectType, node, "objectType", objectType, 1);
+  validate(defs.indexType, node, "indexType", indexType, 1);
+  return node;
+}
+function jsxAttribute(name, value = null) {
+  const node = {
+    type: "JSXAttribute",
+    name,
+    value
+  };
+  const defs = NODE_FIELDS.JSXAttribute;
+  validate(defs.name, node, "name", name, 1);
+  validate(defs.value, node, "value", value, 1);
+  return node;
+}
+function jsxClosingElement(name) {
+  const node = {
+    type: "JSXClosingElement",
+    name
+  };
+  const defs = NODE_FIELDS.JSXClosingElement;
+  validate(defs.name, node, "name", name, 1);
+  return node;
+}
+function jsxElement(openingElement, closingElement = null, children, selfClosing = null) {
+  const node = {
+    type: "JSXElement",
+    openingElement,
+    closingElement,
+    children,
+    selfClosing
+  };
+  const defs = NODE_FIELDS.JSXElement;
+  validate(defs.openingElement, node, "openingElement", openingElement, 1);
+  validate(defs.closingElement, node, "closingElement", closingElement, 1);
+  validate(defs.children, node, "children", children, 1);
+  validate(defs.selfClosing, node, "selfClosing", selfClosing);
+  return node;
+}
+function jsxEmptyExpression() {
+  return {
+    type: "JSXEmptyExpression"
+  };
+}
+function jsxExpressionContainer(expression) {
+  const node = {
+    type: "JSXExpressionContainer",
+    expression
+  };
+  const defs = NODE_FIELDS.JSXExpressionContainer;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function jsxSpreadChild(expression) {
+  const node = {
+    type: "JSXSpreadChild",
+    expression
+  };
+  const defs = NODE_FIELDS.JSXSpreadChild;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function jsxIdentifier(name) {
+  const node = {
+    type: "JSXIdentifier",
+    name
+  };
+  const defs = NODE_FIELDS.JSXIdentifier;
+  validate(defs.name, node, "name", name);
+  return node;
+}
+function jsxMemberExpression(object, property) {
+  const node = {
+    type: "JSXMemberExpression",
+    object,
+    property
+  };
+  const defs = NODE_FIELDS.JSXMemberExpression;
+  validate(defs.object, node, "object", object, 1);
+  validate(defs.property, node, "property", property, 1);
+  return node;
+}
+function jsxNamespacedName(namespace, name) {
+  const node = {
+    type: "JSXNamespacedName",
+    namespace,
+    name
+  };
+  const defs = NODE_FIELDS.JSXNamespacedName;
+  validate(defs.namespace, node, "namespace", namespace, 1);
+  validate(defs.name, node, "name", name, 1);
+  return node;
+}
+function jsxOpeningElement(name, attributes, selfClosing = false) {
+  const node = {
+    type: "JSXOpeningElement",
+    name,
+    attributes,
+    selfClosing
+  };
+  const defs = NODE_FIELDS.JSXOpeningElement;
+  validate(defs.name, node, "name", name, 1);
+  validate(defs.attributes, node, "attributes", attributes, 1);
+  validate(defs.selfClosing, node, "selfClosing", selfClosing);
+  return node;
+}
+function jsxSpreadAttribute(argument) {
+  const node = {
+    type: "JSXSpreadAttribute",
+    argument
+  };
+  const defs = NODE_FIELDS.JSXSpreadAttribute;
+  validate(defs.argument, node, "argument", argument, 1);
+  return node;
+}
+function jsxText(value) {
+  const node = {
+    type: "JSXText",
+    value
+  };
+  const defs = NODE_FIELDS.JSXText;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function jsxFragment(openingFragment, closingFragment, children) {
+  const node = {
+    type: "JSXFragment",
+    openingFragment,
+    closingFragment,
+    children
+  };
+  const defs = NODE_FIELDS.JSXFragment;
+  validate(defs.openingFragment, node, "openingFragment", openingFragment, 1);
+  validate(defs.closingFragment, node, "closingFragment", closingFragment, 1);
+  validate(defs.children, node, "children", children, 1);
+  return node;
+}
+function jsxOpeningFragment() {
+  return {
+    type: "JSXOpeningFragment"
+  };
+}
+function jsxClosingFragment() {
+  return {
+    type: "JSXClosingFragment"
+  };
+}
+function noop() {
+  return {
+    type: "Noop"
+  };
+}
+function placeholder(expectedNode, name) {
+  const node = {
+    type: "Placeholder",
+    expectedNode,
+    name
+  };
+  const defs = NODE_FIELDS.Placeholder;
+  validate(defs.expectedNode, node, "expectedNode", expectedNode);
+  validate(defs.name, node, "name", name, 1);
+  return node;
+}
+function v8IntrinsicIdentifier(name) {
+  const node = {
+    type: "V8IntrinsicIdentifier",
+    name
+  };
+  const defs = NODE_FIELDS.V8IntrinsicIdentifier;
+  validate(defs.name, node, "name", name);
+  return node;
+}
+function argumentPlaceholder() {
+  return {
+    type: "ArgumentPlaceholder"
+  };
+}
+function bindExpression(object, callee) {
+  const node = {
+    type: "BindExpression",
+    object,
+    callee
+  };
+  const defs = NODE_FIELDS.BindExpression;
+  validate(defs.object, node, "object", object, 1);
+  validate(defs.callee, node, "callee", callee, 1);
+  return node;
+}
+function decorator(expression) {
+  const node = {
+    type: "Decorator",
+    expression
+  };
+  const defs = NODE_FIELDS.Decorator;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function doExpression(body, async = false) {
+  const node = {
+    type: "DoExpression",
+    body,
+    async
+  };
+  const defs = NODE_FIELDS.DoExpression;
+  validate(defs.body, node, "body", body, 1);
+  validate(defs.async, node, "async", async);
+  return node;
+}
+function exportDefaultSpecifier(exported) {
+  const node = {
+    type: "ExportDefaultSpecifier",
+    exported
+  };
+  const defs = NODE_FIELDS.ExportDefaultSpecifier;
+  validate(defs.exported, node, "exported", exported, 1);
+  return node;
+}
+function recordExpression(properties) {
+  const node = {
+    type: "RecordExpression",
+    properties
+  };
+  const defs = NODE_FIELDS.RecordExpression;
+  validate(defs.properties, node, "properties", properties, 1);
+  return node;
+}
+function tupleExpression(elements = []) {
+  const node = {
+    type: "TupleExpression",
+    elements
+  };
+  const defs = NODE_FIELDS.TupleExpression;
+  validate(defs.elements, node, "elements", elements, 1);
+  return node;
+}
+function decimalLiteral(value) {
+  const node = {
+    type: "DecimalLiteral",
+    value
+  };
+  const defs = NODE_FIELDS.DecimalLiteral;
+  validate(defs.value, node, "value", value);
+  return node;
+}
+function moduleExpression(body) {
+  const node = {
+    type: "ModuleExpression",
+    body
+  };
+  const defs = NODE_FIELDS.ModuleExpression;
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function topicReference() {
+  return {
+    type: "TopicReference"
+  };
+}
+function pipelineTopicExpression(expression) {
+  const node = {
+    type: "PipelineTopicExpression",
+    expression
+  };
+  const defs = NODE_FIELDS.PipelineTopicExpression;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function pipelineBareFunction(callee) {
+  const node = {
+    type: "PipelineBareFunction",
+    callee
+  };
+  const defs = NODE_FIELDS.PipelineBareFunction;
+  validate(defs.callee, node, "callee", callee, 1);
+  return node;
+}
+function pipelinePrimaryTopicReference() {
+  return {
+    type: "PipelinePrimaryTopicReference"
+  };
+}
+function voidPattern() {
+  return {
+    type: "VoidPattern"
+  };
+}
+function tsParameterProperty(parameter) {
+  const node = {
+    type: "TSParameterProperty",
+    parameter
+  };
+  const defs = NODE_FIELDS.TSParameterProperty;
+  validate(defs.parameter, node, "parameter", parameter, 1);
+  return node;
+}
+function tsDeclareFunction(id = null, typeParameters = null, params, returnType = null) {
+  const node = {
+    type: "TSDeclareFunction",
+    id,
+    typeParameters,
+    params,
+    returnType
+  };
+  const defs = NODE_FIELDS.TSDeclareFunction;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.returnType, node, "returnType", returnType, 1);
+  return node;
+}
+function tsDeclareMethod(decorators = null, key, typeParameters = null, params, returnType = null) {
+  const node = {
+    type: "TSDeclareMethod",
+    decorators,
+    key,
+    typeParameters,
+    params,
+    returnType
+  };
+  const defs = NODE_FIELDS.TSDeclareMethod;
+  validate(defs.decorators, node, "decorators", decorators, 1);
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.params, node, "params", params, 1);
+  validate(defs.returnType, node, "returnType", returnType, 1);
+  return node;
+}
+function tsQualifiedName(left, right) {
+  const node = {
+    type: "TSQualifiedName",
+    left,
+    right
+  };
+  const defs = NODE_FIELDS.TSQualifiedName;
+  validate(defs.left, node, "left", left, 1);
+  validate(defs.right, node, "right", right, 1);
+  return node;
+}
+function tsCallSignatureDeclaration(typeParameters = null, parameters, typeAnnotation = null) {
+  const node = {
+    type: "TSCallSignatureDeclaration",
+    typeParameters,
+    parameters,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSCallSignatureDeclaration;
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.parameters, node, "parameters", parameters, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsConstructSignatureDeclaration(typeParameters = null, parameters, typeAnnotation = null) {
+  const node = {
+    type: "TSConstructSignatureDeclaration",
+    typeParameters,
+    parameters,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSConstructSignatureDeclaration;
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.parameters, node, "parameters", parameters, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsPropertySignature(key, typeAnnotation = null) {
+  const node = {
+    type: "TSPropertySignature",
+    key,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSPropertySignature;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsMethodSignature(key, typeParameters = null, parameters, typeAnnotation = null) {
+  const node = {
+    type: "TSMethodSignature",
+    key,
+    typeParameters,
+    parameters,
+    typeAnnotation,
+    kind: null
+  };
+  const defs = NODE_FIELDS.TSMethodSignature;
+  validate(defs.key, node, "key", key, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.parameters, node, "parameters", parameters, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsIndexSignature(parameters, typeAnnotation = null) {
+  const node = {
+    type: "TSIndexSignature",
+    parameters,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSIndexSignature;
+  validate(defs.parameters, node, "parameters", parameters, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsAnyKeyword() {
+  return {
+    type: "TSAnyKeyword"
+  };
+}
+function tsBooleanKeyword() {
+  return {
+    type: "TSBooleanKeyword"
+  };
+}
+function tsBigIntKeyword() {
+  return {
+    type: "TSBigIntKeyword"
+  };
+}
+function tsIntrinsicKeyword() {
+  return {
+    type: "TSIntrinsicKeyword"
+  };
+}
+function tsNeverKeyword() {
+  return {
+    type: "TSNeverKeyword"
+  };
+}
+function tsNullKeyword() {
+  return {
+    type: "TSNullKeyword"
+  };
+}
+function tsNumberKeyword() {
+  return {
+    type: "TSNumberKeyword"
+  };
+}
+function tsObjectKeyword() {
+  return {
+    type: "TSObjectKeyword"
+  };
+}
+function tsStringKeyword() {
+  return {
+    type: "TSStringKeyword"
+  };
+}
+function tsSymbolKeyword() {
+  return {
+    type: "TSSymbolKeyword"
+  };
+}
+function tsUndefinedKeyword() {
+  return {
+    type: "TSUndefinedKeyword"
+  };
+}
+function tsUnknownKeyword() {
+  return {
+    type: "TSUnknownKeyword"
+  };
+}
+function tsVoidKeyword() {
+  return {
+    type: "TSVoidKeyword"
+  };
+}
+function tsThisType() {
+  return {
+    type: "TSThisType"
+  };
+}
+function tsFunctionType(typeParameters = null, parameters, typeAnnotation = null) {
+  const node = {
+    type: "TSFunctionType",
+    typeParameters,
+    parameters,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSFunctionType;
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.parameters, node, "parameters", parameters, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsConstructorType(typeParameters = null, parameters, typeAnnotation = null) {
+  const node = {
+    type: "TSConstructorType",
+    typeParameters,
+    parameters,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSConstructorType;
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.parameters, node, "parameters", parameters, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsTypeReference(typeName, typeParameters = null) {
+  const node = {
+    type: "TSTypeReference",
+    typeName,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.TSTypeReference;
+  validate(defs.typeName, node, "typeName", typeName, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function tsTypePredicate(parameterName, typeAnnotation = null, asserts = null) {
+  const node = {
+    type: "TSTypePredicate",
+    parameterName,
+    typeAnnotation,
+    asserts
+  };
+  const defs = NODE_FIELDS.TSTypePredicate;
+  validate(defs.parameterName, node, "parameterName", parameterName, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  validate(defs.asserts, node, "asserts", asserts);
+  return node;
+}
+function tsTypeQuery(exprName, typeParameters = null) {
+  const node = {
+    type: "TSTypeQuery",
+    exprName,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.TSTypeQuery;
+  validate(defs.exprName, node, "exprName", exprName, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function tsTypeLiteral(members) {
+  const node = {
+    type: "TSTypeLiteral",
+    members
+  };
+  const defs = NODE_FIELDS.TSTypeLiteral;
+  validate(defs.members, node, "members", members, 1);
+  return node;
+}
+function tsArrayType(elementType) {
+  const node = {
+    type: "TSArrayType",
+    elementType
+  };
+  const defs = NODE_FIELDS.TSArrayType;
+  validate(defs.elementType, node, "elementType", elementType, 1);
+  return node;
+}
+function tsTupleType(elementTypes) {
+  const node = {
+    type: "TSTupleType",
+    elementTypes
+  };
+  const defs = NODE_FIELDS.TSTupleType;
+  validate(defs.elementTypes, node, "elementTypes", elementTypes, 1);
+  return node;
+}
+function tsOptionalType(typeAnnotation) {
+  const node = {
+    type: "TSOptionalType",
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSOptionalType;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsRestType(typeAnnotation) {
+  const node = {
+    type: "TSRestType",
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSRestType;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsNamedTupleMember(label, elementType, optional = false) {
+  const node = {
+    type: "TSNamedTupleMember",
+    label,
+    elementType,
+    optional
+  };
+  const defs = NODE_FIELDS.TSNamedTupleMember;
+  validate(defs.label, node, "label", label, 1);
+  validate(defs.elementType, node, "elementType", elementType, 1);
+  validate(defs.optional, node, "optional", optional);
+  return node;
+}
+function tsUnionType(types) {
+  const node = {
+    type: "TSUnionType",
+    types
+  };
+  const defs = NODE_FIELDS.TSUnionType;
+  validate(defs.types, node, "types", types, 1);
+  return node;
+}
+function tsIntersectionType(types) {
+  const node = {
+    type: "TSIntersectionType",
+    types
+  };
+  const defs = NODE_FIELDS.TSIntersectionType;
+  validate(defs.types, node, "types", types, 1);
+  return node;
+}
+function tsConditionalType(checkType, extendsType, trueType, falseType) {
+  const node = {
+    type: "TSConditionalType",
+    checkType,
+    extendsType,
+    trueType,
+    falseType
+  };
+  const defs = NODE_FIELDS.TSConditionalType;
+  validate(defs.checkType, node, "checkType", checkType, 1);
+  validate(defs.extendsType, node, "extendsType", extendsType, 1);
+  validate(defs.trueType, node, "trueType", trueType, 1);
+  validate(defs.falseType, node, "falseType", falseType, 1);
+  return node;
+}
+function tsInferType(typeParameter) {
+  const node = {
+    type: "TSInferType",
+    typeParameter
+  };
+  const defs = NODE_FIELDS.TSInferType;
+  validate(defs.typeParameter, node, "typeParameter", typeParameter, 1);
+  return node;
+}
+function tsParenthesizedType(typeAnnotation) {
+  const node = {
+    type: "TSParenthesizedType",
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSParenthesizedType;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsTypeOperator(typeAnnotation, operator = "keyof") {
+  const node = {
+    type: "TSTypeOperator",
+    typeAnnotation,
+    operator
+  };
+  const defs = NODE_FIELDS.TSTypeOperator;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  validate(defs.operator, node, "operator", operator);
+  return node;
+}
+function tsIndexedAccessType(objectType, indexType) {
+  const node = {
+    type: "TSIndexedAccessType",
+    objectType,
+    indexType
+  };
+  const defs = NODE_FIELDS.TSIndexedAccessType;
+  validate(defs.objectType, node, "objectType", objectType, 1);
+  validate(defs.indexType, node, "indexType", indexType, 1);
+  return node;
+}
+function tsMappedType(typeParameter, typeAnnotation = null, nameType = null) {
+  const node = {
+    type: "TSMappedType",
+    typeParameter,
+    typeAnnotation,
+    nameType
+  };
+  const defs = NODE_FIELDS.TSMappedType;
+  validate(defs.typeParameter, node, "typeParameter", typeParameter, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  validate(defs.nameType, node, "nameType", nameType, 1);
+  return node;
+}
+function tsTemplateLiteralType(quasis, types) {
+  const node = {
+    type: "TSTemplateLiteralType",
+    quasis,
+    types
+  };
+  const defs = NODE_FIELDS.TSTemplateLiteralType;
+  validate(defs.quasis, node, "quasis", quasis, 1);
+  validate(defs.types, node, "types", types, 1);
+  return node;
+}
+function tsLiteralType(literal) {
+  const node = {
+    type: "TSLiteralType",
+    literal
+  };
+  const defs = NODE_FIELDS.TSLiteralType;
+  validate(defs.literal, node, "literal", literal, 1);
+  return node;
+}
+function tsExpressionWithTypeArguments(expression, typeParameters = null) {
+  const node = {
+    type: "TSExpressionWithTypeArguments",
+    expression,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.TSExpressionWithTypeArguments;
+  validate(defs.expression, node, "expression", expression, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function tsInterfaceDeclaration(id, typeParameters = null, _extends = null, body) {
+  const node = {
+    type: "TSInterfaceDeclaration",
+    id,
+    typeParameters,
+    extends: _extends,
+    body
+  };
+  const defs = NODE_FIELDS.TSInterfaceDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.extends, node, "extends", _extends, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function tsInterfaceBody(body) {
+  const node = {
+    type: "TSInterfaceBody",
+    body
+  };
+  const defs = NODE_FIELDS.TSInterfaceBody;
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function tsTypeAliasDeclaration(id, typeParameters = null, typeAnnotation) {
+  const node = {
+    type: "TSTypeAliasDeclaration",
+    id,
+    typeParameters,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSTypeAliasDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsInstantiationExpression(expression, typeParameters = null) {
+  const node = {
+    type: "TSInstantiationExpression",
+    expression,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.TSInstantiationExpression;
+  validate(defs.expression, node, "expression", expression, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function tsAsExpression(expression, typeAnnotation) {
+  const node = {
+    type: "TSAsExpression",
+    expression,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSAsExpression;
+  validate(defs.expression, node, "expression", expression, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsSatisfiesExpression(expression, typeAnnotation) {
+  const node = {
+    type: "TSSatisfiesExpression",
+    expression,
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSSatisfiesExpression;
+  validate(defs.expression, node, "expression", expression, 1);
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsTypeAssertion(typeAnnotation, expression) {
+  const node = {
+    type: "TSTypeAssertion",
+    typeAnnotation,
+    expression
+  };
+  const defs = NODE_FIELDS.TSTypeAssertion;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function tsEnumBody(members) {
+  const node = {
+    type: "TSEnumBody",
+    members
+  };
+  const defs = NODE_FIELDS.TSEnumBody;
+  validate(defs.members, node, "members", members, 1);
+  return node;
+}
+function tsEnumDeclaration(id, members) {
+  const node = {
+    type: "TSEnumDeclaration",
+    id,
+    members
+  };
+  const defs = NODE_FIELDS.TSEnumDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.members, node, "members", members, 1);
+  return node;
+}
+function tsEnumMember(id, initializer = null) {
+  const node = {
+    type: "TSEnumMember",
+    id,
+    initializer
+  };
+  const defs = NODE_FIELDS.TSEnumMember;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.initializer, node, "initializer", initializer, 1);
+  return node;
+}
+function tsModuleDeclaration(id, body) {
+  const node = {
+    type: "TSModuleDeclaration",
+    id,
+    body,
+    kind: null
+  };
+  const defs = NODE_FIELDS.TSModuleDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function tsModuleBlock(body) {
+  const node = {
+    type: "TSModuleBlock",
+    body
+  };
+  const defs = NODE_FIELDS.TSModuleBlock;
+  validate(defs.body, node, "body", body, 1);
+  return node;
+}
+function tsImportType(argument, qualifier = null, typeParameters = null) {
+  const node = {
+    type: "TSImportType",
+    argument,
+    qualifier,
+    typeParameters
+  };
+  const defs = NODE_FIELDS.TSImportType;
+  validate(defs.argument, node, "argument", argument, 1);
+  validate(defs.qualifier, node, "qualifier", qualifier, 1);
+  validate(defs.typeParameters, node, "typeParameters", typeParameters, 1);
+  return node;
+}
+function tsImportEqualsDeclaration(id, moduleReference) {
+  const node = {
+    type: "TSImportEqualsDeclaration",
+    id,
+    moduleReference,
+    isExport: null
+  };
+  const defs = NODE_FIELDS.TSImportEqualsDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  validate(defs.moduleReference, node, "moduleReference", moduleReference, 1);
+  return node;
+}
+function tsExternalModuleReference(expression) {
+  const node = {
+    type: "TSExternalModuleReference",
+    expression
+  };
+  const defs = NODE_FIELDS.TSExternalModuleReference;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function tsNonNullExpression(expression) {
+  const node = {
+    type: "TSNonNullExpression",
+    expression
+  };
+  const defs = NODE_FIELDS.TSNonNullExpression;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function tsExportAssignment(expression) {
+  const node = {
+    type: "TSExportAssignment",
+    expression
+  };
+  const defs = NODE_FIELDS.TSExportAssignment;
+  validate(defs.expression, node, "expression", expression, 1);
+  return node;
+}
+function tsNamespaceExportDeclaration(id) {
+  const node = {
+    type: "TSNamespaceExportDeclaration",
+    id
+  };
+  const defs = NODE_FIELDS.TSNamespaceExportDeclaration;
+  validate(defs.id, node, "id", id, 1);
+  return node;
+}
+function tsTypeAnnotation(typeAnnotation) {
+  const node = {
+    type: "TSTypeAnnotation",
+    typeAnnotation
+  };
+  const defs = NODE_FIELDS.TSTypeAnnotation;
+  validate(defs.typeAnnotation, node, "typeAnnotation", typeAnnotation, 1);
+  return node;
+}
+function tsTypeParameterInstantiation(params) {
+  const node = {
+    type: "TSTypeParameterInstantiation",
+    params
+  };
+  const defs = NODE_FIELDS.TSTypeParameterInstantiation;
+  validate(defs.params, node, "params", params, 1);
+  return node;
+}
+function tsTypeParameterDeclaration(params) {
+  const node = {
+    type: "TSTypeParameterDeclaration",
+    params
+  };
+  const defs = NODE_FIELDS.TSTypeParameterDeclaration;
+  validate(defs.params, node, "params", params, 1);
+  return node;
+}
+function tsTypeParameter(constraint = null, _default = null, name) {
+  const node = {
+    type: "TSTypeParameter",
+    constraint,
+    default: _default,
+    name
+  };
+  const defs = NODE_FIELDS.TSTypeParameter;
+  validate(defs.constraint, node, "constraint", constraint, 1);
+  validate(defs.default, node, "default", _default, 1);
+  validate(defs.name, node, "name", name);
+  return node;
+}
+function NumberLiteral(value) {
+  (0, _deprecationWarning.default)("NumberLiteral", "NumericLiteral", "The node type ");
+  return numericLiteral(value);
+}
+function RegexLiteral(pattern, flags = "") {
+  (0, _deprecationWarning.default)("RegexLiteral", "RegExpLiteral", "The node type ");
+  return regExpLiteral(pattern, flags);
+}
+function RestProperty(argument) {
+  (0, _deprecationWarning.default)("RestProperty", "RestElement", "The node type ");
+  return restElement(argument);
+}
+function SpreadProperty(argument) {
+  (0, _deprecationWarning.default)("SpreadProperty", "SpreadElement", "The node type ");
+  return spreadElement(argument);
+}
+
+//# sourceMappingURL=lowercase.js.map

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
node_modules/@babel/types/lib/builders/generated/lowercase.js.map


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 5 - 0
node_modules/@babel/types/lib/builders/generated/uppercase.js


Vissa filer visades inte eftersom för många filer har ändrats