Просмотр исходного кода

feat: migrate xiaoshu app to Angular 18

彭峰 2 месяцев назад
Сommit
018b3f181c
100 измененных файлов с 24455 добавлено и 0 удалено
  1. 17 0
      .editorconfig
  2. 42 0
      .gitignore
  3. 4 0
      .vscode/extensions.json
  4. 20 0
      .vscode/launch.json
  5. 42 0
      .vscode/tasks.json
  6. 66 0
      README.md
  7. 106 0
      angular.json
  8. 84 0
      design-qa.md
  9. 14725 0
      package-lock.json
  10. 45 0
      package.json
  11. 25 0
      proxy.conf.cjs
  12. BIN
      public/favicon.ico
  13. BIN
      public/static/bytedesk/cuw/cuw_normal.png
  14. BIN
      public/static/bytedesk/cuw/cuw_pressed.png
  15. BIN
      public/static/bytedesk/rate/down.png
  16. BIN
      public/static/bytedesk/rate/down_active.png
  17. BIN
      public/static/bytedesk/rate/rate_pic.png
  18. BIN
      public/static/bytedesk/rate/rate_star.png
  19. BIN
      public/static/bytedesk/rate/rate_star_active.png
  20. BIN
      public/static/bytedesk/rate/up.png
  21. BIN
      public/static/bytedesk/rate/up_active.png
  22. 363 0
      public/static/common/js/touch-emulator.js
  23. BIN
      public/static/game/plane.png
  24. BIN
      public/static/game/xj.png
  25. BIN
      public/static/images/ai-reading-journey.png
  26. BIN
      public/static/images/demo1.jpg
  27. BIN
      public/static/images/demo2.jpg
  28. BIN
      public/static/images/foot_icon1.png
  29. BIN
      public/static/images/foot_icon1_active.png
  30. BIN
      public/static/images/foot_icon2.png
  31. BIN
      public/static/images/foot_icon2_active.png
  32. BIN
      public/static/images/foot_icon3.png
  33. BIN
      public/static/images/foot_icon3_active.png
  34. BIN
      public/static/images/foot_icon4.png
  35. BIN
      public/static/images/foot_icon4_active.png
  36. 0 0
      public/static/images/homeicon1.svg
  37. 0 0
      public/static/images/homeicon2.svg
  38. 0 0
      public/static/images/homeicon3.svg
  39. BIN
      public/static/images/i4.png
  40. 1 0
      public/static/images/index_list1.svg
  41. 1 0
      public/static/images/index_list2.svg
  42. 0 0
      public/static/images/index_list3.svg
  43. 1 0
      public/static/images/index_list4.svg
  44. BIN
      public/static/images/logo.png
  45. 29 0
      public/static/images/logo.svg
  46. BIN
      public/static/images/perfect.png
  47. 8 0
      public/static/zico.scss
  48. 99 0
      scripts/generate-source-parity.mjs
  49. 1 0
      src/app/app.component.html
  50. 4 0
      src/app/app.component.scss
  51. 30 0
      src/app/app.component.spec.ts
  52. 12 0
      src/app/app.component.ts
  53. 13 0
      src/app/app.config.ts
  54. 75 0
      src/app/app.routes.ts
  55. 70 0
      src/app/core/all-routes.integration.spec.ts
  56. 92 0
      src/app/core/anti-forget.spec.ts
  57. 252 0
      src/app/core/anti-forget.ts
  58. 35 0
      src/app/core/api.service.spec.ts
  59. 94 0
      src/app/core/api.service.ts
  60. 27 0
      src/app/core/app.constants.ts
  61. 11 0
      src/app/core/auth.guard.ts
  62. 29 0
      src/app/core/auth.service.ts
  63. 34 0
      src/app/core/business-rules.spec.ts
  64. 212 0
      src/app/core/business-rules.ts
  65. 108 0
      src/app/core/bytedesk.service.spec.ts
  66. 322 0
      src/app/core/bytedesk.service.ts
  67. 22 0
      src/app/core/external-reading.spec.ts
  68. 22 0
      src/app/core/external-reading.ts
  69. 42 0
      src/app/core/form-payload.spec.ts
  70. 210 0
      src/app/core/form-payload.ts
  71. 358 0
      src/app/core/home-learning-modules.integration.spec.ts
  72. 113 0
      src/app/core/models.ts
  73. 26 0
      src/app/core/page-catalog.spec.ts
  74. 116 0
      src/app/core/page-catalog.ts
  75. 76 0
      src/app/core/payment.service.ts
  76. 52 0
      src/app/core/platform.service.ts
  77. 89 0
      src/app/core/session.service.ts
  78. 4168 0
      src/app/core/source-parity.generated.ts
  79. 34 0
      src/app/core/source-parity.spec.ts
  80. 65 0
      src/app/features/appointment/appointment-page.component.html
  81. 34 0
      src/app/features/appointment/appointment-page.component.scss
  82. 384 0
      src/app/features/appointment/appointment-page.component.ts
  83. 8 0
      src/app/features/article/article-page.component.html
  84. 14 0
      src/app/features/article/article-page.component.scss
  85. 78 0
      src/app/features/article/article-page.component.ts
  86. 30 0
      src/app/features/auth/login-page.component.html
  87. 21 0
      src/app/features/auth/login-page.component.scss
  88. 140 0
      src/app/features/auth/login-page.component.ts
  89. 37 0
      src/app/features/checkout/checkout-page.component.html
  90. 35 0
      src/app/features/checkout/checkout-page.component.scss
  91. 230 0
      src/app/features/checkout/checkout-page.component.ts
  92. 51 0
      src/app/features/course-center/course-center-page.component.html
  93. 57 0
      src/app/features/course-center/course-center-page.component.scss
  94. 177 0
      src/app/features/course-center/course-center-page.component.ts
  95. 232 0
      src/app/features/english-reading/english-reading.service.ts
  96. 157 0
      src/app/features/english-reading/fmode-ai.service.ts
  97. 48 0
      src/app/features/english-reading/ncloud-reading.service.spec.ts
  98. 139 0
      src/app/features/english-reading/ncloud-reading.service.ts
  99. 33 0
      src/app/features/english-reading/reading-exercise.component.html
  100. 58 0
      src/app/features/english-reading/reading-exercise.component.scss

+ 17 - 0
.editorconfig

@@ -0,0 +1,17 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.ts]
+quote_type = single
+ij_typescript_use_double_quotes = false
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false

+ 42 - 0
.gitignore

@@ -0,0 +1,42 @@
+# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
+
+# Compiled output
+/dist
+/tmp
+/out-tsc
+/bazel-out
+
+# Node
+/node_modules
+npm-debug.log
+yarn-error.log
+
+# IDEs and editors
+.idea/
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# Visual Studio Code
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# Miscellaneous
+/.angular/cache
+.sass-cache/
+/connect.lock
+/coverage
+/libpeerconnection.log
+testem.log
+/typings
+
+# System files
+.DS_Store
+Thumbs.db

+ 4 - 0
.vscode/extensions.json

@@ -0,0 +1,4 @@
+{
+  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
+  "recommendations": ["angular.ng-template"]
+}

+ 20 - 0
.vscode/launch.json

@@ -0,0 +1,20 @@
+{
+  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "name": "ng serve",
+      "type": "chrome",
+      "request": "launch",
+      "preLaunchTask": "npm: start",
+      "url": "http://localhost:4200/"
+    },
+    {
+      "name": "ng test",
+      "type": "chrome",
+      "request": "launch",
+      "preLaunchTask": "npm: test",
+      "url": "http://localhost:9876/debug.html"
+    }
+  ]
+}

+ 42 - 0
.vscode/tasks.json

@@ -0,0 +1,42 @@
+{
+  // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
+  "version": "2.0.0",
+  "tasks": [
+    {
+      "type": "npm",
+      "script": "start",
+      "isBackground": true,
+      "problemMatcher": {
+        "owner": "typescript",
+        "pattern": "$tsc",
+        "background": {
+          "activeOnStart": true,
+          "beginsPattern": {
+            "regexp": "(.*?)"
+          },
+          "endsPattern": {
+            "regexp": "bundle generation complete"
+          }
+        }
+      }
+    },
+    {
+      "type": "npm",
+      "script": "test",
+      "isBackground": true,
+      "problemMatcher": {
+        "owner": "typescript",
+        "pattern": "$tsc",
+        "background": {
+          "activeOnStart": true,
+          "beginsPattern": {
+            "regexp": "(.*?)"
+          },
+          "endsPattern": {
+            "regexp": "bundle generation complete"
+          }
+        }
+      }
+    }
+  ]
+}

+ 66 - 0
README.md

@@ -0,0 +1,66 @@
+# 小树陪练 Angular H5
+
+这是从同级 `xiaoshu-uniapp` 迁移出的 Angular 18 Web/H5 项目。项目保留原有 95 条业务路由,并使用 Angular Router、Signals、HttpClient 与独立组件重新实现登录、学习、复习、测试、游戏、商城、支付、订单、团队、商户、表单与会员业务。
+
+## 环境
+
+- Node.js 18.19+(当前验证版本为 Node.js 22)
+- npm 9+
+- Angular 18.2
+
+## 本地运行
+
+```bash
+npm install
+npm start -- --host 127.0.0.1 --port 4201
+```
+
+浏览器打开 `http://127.0.0.1:4201/`。开发服务器通过 `proxy.conf.cjs` 将 `/backend` 转发到线上 UniApp 当前使用的 `https://test.fmode.cn/engxs`,并将客服、NCloud 与 AI 请求转发到对应服务,避免本地 H5 调试时的跨域问题。
+
+AI 阅读需要在启动进程中提供服务端环境变量,令牌不会进入浏览器构建产物:
+
+```bash
+FMODE_API_TOKEN="你的令牌" npm start -- --host 127.0.0.1 --port 4201
+```
+
+## 构建与测试
+
+```bash
+npm run build
+npm run test:ci
+```
+
+生产文件输出到 `dist/xiaoshu-angular/browser/`。部署时需要把 Angular 路由回退到 `index.html`,并确认原后端允许生产域名跨域;也可以在 Web 服务器上继续反向代理 `/backend`。
+
+## 迁移验收
+
+- `pages.json` 中 95/95 条路由已注册,路径保持不变。
+- 生成的源码对照清单跟踪 91 个字面 API 动作及 Bytedesk `httpApi.*` 外部业务调用,`npm run parity:check` 可检测源页面变化。
+- 原生微信授权接口 `user_sync2` 依赖 uni-app/微信容器,H5 改用短信登录、账号密码登录与注册。
+- 收货地址、发票、商品、库存、结算、发货、售后、订单评价、提现账户、实名认证和反馈均保留原后端载荷格式。
+- 生产构建通过,页面按路由懒加载;45 项自动测试覆盖路由对照、业务载荷、表单载荷、Bytedesk 会话/评价/留言协议、外部阅读参数和会员权益统计。
+- 公开页已在 390 x 844 与 1440 x 900 视口回归,无横向溢出或 Angular 运行时错误;未登录访问受保护页面会保留 `returnUrl` 并跳转登录页。
+- 客服页已迁移原 UniApp 使用的 Bytedesk 匿名访客登录、工作组会话、历史消息轮询、智能客服问答、图片上传与文件上传协议。2026-08-08 实测发现 `uniapp.bytedesk.com` 已停用原 `/visitor/api/*` 接口并改为返回微语官网 HTML,因此旧 UniApp 与迁移页目前都无法建立真实会话;页面会明确显示旧服务已停用。
+- 满意度评价与离线留言不再复用业务留言表单,分别保持原 `/api/rate/detail`、`/api/rate/do`、`/api/leavemsg/save` 协议及 `tid/wid/aid/type/invite` 会话参数;旧 Bytedesk 服务停用时会显示真实错误。
+- 用户中心和游戏中心入口按当前 UniApp 模板恢复:用户中心只显示源页面实际开放的服务,游戏中心四个年级入口继续提示“正在开发中”。
+- 页面会区分“没有业务数据”和“接口加载失败”;依赖订单或详情上下文的表单缺少 `id` 时禁止提交并提示从正确入口进入。
+- 学习记录中的“阅读”入口已完整内置:从记录 `xxqs` 提取单词后进入 Angular 阅读设置、AI 文章生成、词汇补全、选择题、历史成绩和 PDF 导出页面,不再加载 `app.fmode.cn` iframe。
+- 旧入口 `/pages/iframe/index` 保留参数兼容,原 `params` 中的单词列表与 NCloud 会话会被内部页面接收。
+- 独立项目原首页迁移到 `/english`:生词阅读入口可用,“外文源刊解析”和“智慧陪读陪练”继续按源代码显示“暂未开放”。
+- 阅读业务保持源仓库规则:原默认参数、随机题材与类型、相同词汇三篇上限、重复文章判断、120/180 秒任务超时、最多五次重试、最近答题恢复、题目与选项洗牌、成绩记录、10 条历史分页、满分后新文章和微信 PDF 提示均已迁移。
+
+涉及支付、短信、上传、写入和登录后数据的真实联调,需要有效测试账号及可用的后端业务数据。自动测试使用模拟会话和模拟 API,避免在验收中产生真实订单、短信或数据变更。
+
+原排行榜动态调用的 `ranklist_1` 至 `ranklist_5` 在当前后端返回“接口不存在”,H5 保留原请求并显示空状态。
+
+## 第三方数据边界
+
+- 客服迁移代码仅允许向 `uniapp.bytedesk.com`、`upload.bytedesk.com` 发送聊天内容和所选附件。当前匿名会话握手会因旧接口下线而中止,不会继续发送聊天或上传文件。
+- AI 阅读将手机号、昵称、NCloud 登录凭据、文章、题目、答案、成绩和历史记录发送至 `server.xiaoshu.pro`,并将单词列表、年级、题材、文章及提示词发送至 `api.fmode.cn`。AI 密钥与 Parse `masterKey` 只保留在服务端,不存放在前端源码或浏览器存储中。
+- 上述流程已根据 2026-08-08 的明确授权启用。应用不会在该流程中自行向其他 NCloud 域名提交账号或密码。
+
+生产部署必须配置 `/fmode-api` 到 `https://api.fmode.cn` 的服务端反向代理并注入授权头,同时配置 `/ncloud` 到 `https://server.xiaoshu.pro/parse`;不要把 `FMODE_API_TOKEN` 或 Parse `masterKey` 写进 Angular 环境文件。
+
+为避免扩大私有数据读取范围,迁移版在两处保留了比源仓库更严格的限制:答题记录始终限定当前用户,PDF 页面继续要求当前应用登录;源仓库中未带用户条件的历史查询和公开 PDF ID 读取没有照搬。H5 目标也不再执行原 iframe 中仅供 APP-PLUS 使用的 `uni.postMessage` PDF 回传。
+
+恢复客服需要在新版微语后台取得新的 `org`、`sid`,并另行授权新版 H5 客服实际使用的域名;当前官方接入格式已不再使用旧版 `subDomain`、`workGroupId`。

+ 106 - 0
angular.json

@@ -0,0 +1,106 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "version": 1,
+  "cli": {
+    "packageManager": "npm",
+    "analytics": false
+  },
+  "newProjectRoot": "projects",
+  "projects": {
+    "xiaoshu-angular": {
+      "projectType": "application",
+      "schematics": {
+        "@schematics/angular:component": {
+          "style": "scss"
+        }
+      },
+      "root": "",
+      "sourceRoot": "src",
+      "prefix": "app",
+      "architect": {
+        "build": {
+          "builder": "@angular-devkit/build-angular:application",
+          "options": {
+            "outputPath": "dist/xiaoshu-angular",
+            "index": "src/index.html",
+            "browser": "src/main.ts",
+            "polyfills": [
+              "zone.js"
+            ],
+            "tsConfig": "tsconfig.app.json",
+            "inlineStyleLanguage": "scss",
+            "assets": [
+              {
+                "glob": "**/*",
+                "input": "public"
+              }
+            ],
+            "styles": [
+              "src/styles.scss"
+            ],
+            "scripts": []
+          },
+          "configurations": {
+            "production": {
+              "budgets": [
+                {
+                  "type": "initial",
+                  "maximumWarning": "500kB",
+                  "maximumError": "1MB"
+                },
+                {
+                  "type": "anyComponentStyle",
+                  "maximumWarning": "8kB",
+                  "maximumError": "20kB"
+                }
+              ],
+              "outputHashing": "all"
+            },
+            "development": {
+              "optimization": false,
+              "extractLicenses": false,
+              "sourceMap": true
+            }
+          },
+          "defaultConfiguration": "production"
+        },
+        "serve": {
+          "builder": "@angular-devkit/build-angular:dev-server",
+          "configurations": {
+            "production": {
+              "buildTarget": "xiaoshu-angular:build:production"
+            },
+            "development": {
+              "buildTarget": "xiaoshu-angular:build:development"
+            }
+          },
+          "defaultConfiguration": "development"
+        },
+        "extract-i18n": {
+          "builder": "@angular-devkit/build-angular:extract-i18n"
+        },
+        "test": {
+          "builder": "@angular-devkit/build-angular:karma",
+          "options": {
+            "polyfills": [
+              "zone.js",
+              "zone.js/testing"
+            ],
+            "tsConfig": "tsconfig.spec.json",
+            "inlineStyleLanguage": "scss",
+            "assets": [
+              {
+                "glob": "**/*",
+                "input": "public"
+              }
+            ],
+            "styles": [
+              "src/styles.scss"
+            ],
+            "scripts": []
+          }
+        }
+      }
+    }
+  }
+}

+ 84 - 0
design-qa.md

@@ -0,0 +1,84 @@
+# Design QA: 21 天抗遗忘原理图
+
+## Evidence
+
+- Source curve truth: `/var/folders/yx/cq4gbk4x6w702x_slllw80mw0000gn/T/codex-clipboard-6e66d742-2284-4c3b-9e41-32ce655b6d0d.png` (2252 x 1452 px).
+- Source module layout: `/var/folders/yx/cq4gbk4x6w702x_slllw80mw0000gn/T/TemporaryItems/NSIRD_screencaptureui_qEDcyG/截屏2026-08-10 22.18.48.png` (1008 x 514 px).
+- Implementation route: `http://127.0.0.1:4202/pages/stu/stu_calendar`
+- Full implementation screenshot: `/private/tmp/xiaoshu-anti-forget-background-mobile.png`
+- Focused module screenshot: `/private/tmp/xiaoshu-anti-forget-background-hero.png`
+- Viewport: 390 x 844 CSS px, device pixel ratio 1.
+- Implementation dimensions: 390 x 844 px full view; 390 x 390 px focused module crop.
+- State: authenticated student, August 2026 calendar, current date selected.
+- Density normalization: both implementation captures are 1x. The source is a conceptual landscape diagram, so the focused comparison evaluates its curve relationships and hierarchy rather than pixel-identical proportions.
+
+## Full-View Comparison
+
+- The reference concept is contained inside the existing brand-green overview panel without changing the calendar, statistics, route, or task interactions.
+- The enlarged chart preserves the mobile page hierarchy and introduces no horizontal overflow at 390 px.
+- Calendar date selection was tested: selecting August 11 updated the detail heading to `8月11日周二`, and returning to August 10 restored the current selection.
+
+## Focused Comparison
+
+- The source module screenshot and focused implementation were opened together in one comparison input.
+- The implementation reproduces the source's four essential relationships: a rapidly descending natural-forgetting curve, a rising long-term-memory curve, five increasingly spaced review curves, and vertical recovery from each intersection toward full retention.
+- Twenty-one timeline nodes remain visible and animate sequentially; five review intersections have independent recovery animations.
+- The chart now occupies the entire green overview as its background. The heading, completion score, curve labels, and statistics are overlaid within the same visual field; the previous inner chart frame has been removed.
+
+## Findings
+
+- No actionable P0, P1, or P2 differences remain.
+- Typography: compact UI labels use the application's existing font stack and remain readable at the target viewport. Reference handwriting was intentionally not copied because this is an operational app chart.
+- Spacing and layout: the chart fills the 390 px overview background, while the 390 px-tall module keeps the calendar immediately visible below it.
+- Colors: source semantic roles are retained with brand-adjusted tokens: orange for forgetting/review decay, green for strengthened memory, and amber for review intersections.
+- Image quality: the reference is recreated as responsive chart data rather than embedded as a raster image, preserving sharpness at mobile and desktop widths.
+- Copy: labels use concise product language (`自然遗忘`, `长期记忆`, `复习1–5`) while preserving the reference meaning.
+
+## Comparison History
+
+1. Earlier implementation used a repeated sawtooth line. This did not express the source's simultaneous learning and forgetting curves.
+2. Replaced the sawtooth with layered forgetting, learning, review-decay, and recovery paths; added five review intersections and a 21-day pulse timeline.
+3. Post-fix evidence at 390 x 844 showed no overflow, preserved page hierarchy, and matched the reference's conceptual structure.
+4. The graph was enlarged from a framed subpanel into the complete overview background; animation timing changed from a 3.8-second loop to a 12-second daily loop and a 15-second review-recovery loop.
+
+## Follow-up Polish
+
+- P3: Review labels are intentionally compact on 390 px screens; a future expanded chart modal could provide larger explanatory labels without making the calendar harder to reach.
+
+final result: passed
+
+---
+
+# Design QA: 学习记录 AI 阅读入口
+
+## Evidence
+
+- Generated visual source: `/Users/mac/.codex/generated_images/019fc67f-8cbd-7212-818f-b47b09c6cf12/exec-6b9c4504-70be-4c93-bf0c-5fad48e3cdc8.png` (1774 x 887 px).
+- Production asset: `/Users/mac/Desktop/编程项目/xiaoshu-angular/public/static/images/ai-reading-journey.png`.
+- Implementation route: `http://127.0.0.1:4202/pages/his/his`.
+- Mobile screenshot: `/private/tmp/xiaoshu-ai-reading-mobile.png` at 390 x 844 CSS px.
+- Tablet screenshot: `/private/tmp/xiaoshu-ai-reading-tablet.png` at 768 x 1024 CSS px.
+- Additional responsive check: 430 x 932 CSS px.
+- State: authenticated student with a latest eligible learning record containing 30 words.
+
+## Comparison
+
+- The generated source and both implementation screenshots were inspected together in one comparison input.
+- The illustration retains its intended left-side negative space and right-side learning-to-reading-to-grading story at both target sizes.
+- The banner uses the existing green brand palette, 8 px container radius, restrained shadow, and the same 14 px page gutter as the calendar and record modules.
+- The first viewport now explains the complete value chain: learned words, generated reading, completed answers, and AI grading.
+
+## Behavior
+
+- The primary CTA selects the newest learning record that contains a readable word list and sends its real `GeneralID` and words to the existing `/pages/iframe/index` workflow.
+- Live verification used record `827482` with 30 words, reached the AI reading setup screen, and returned to `/pages/his/his` without changing the existing back-stack contract.
+- The CTA is disabled with explanatory copy when no eligible learning record exists.
+
+## Findings
+
+- No horizontal overflow at 390, 430, or 768 px.
+- CTA content fits inside its 44 px touch target without wrapping or clipping.
+- Image loaded at its natural 1774 x 887 resolution and remained crisp after responsive cropping.
+- No actionable P0, P1, or P2 visual differences remain.
+
+final result: passed

+ 14725 - 0
package-lock.json

@@ -0,0 +1,14725 @@
+{
+  "name": "xiaoshu-angular",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "xiaoshu-angular",
+      "version": "0.0.0",
+      "dependencies": {
+        "@angular/animations": "^18.2.0",
+        "@angular/common": "^18.2.0",
+        "@angular/compiler": "^18.2.0",
+        "@angular/core": "^18.2.0",
+        "@angular/forms": "^18.2.0",
+        "@angular/platform-browser": "^18.2.0",
+        "@angular/platform-browser-dynamic": "^18.2.0",
+        "@angular/router": "^18.2.0",
+        "html2pdf.js": "^0.10.3",
+        "lucide-angular": "^0.468.0",
+        "mustache": "^4.2.0",
+        "rxjs": "~7.8.0",
+        "tslib": "^2.3.0",
+        "zone.js": "~0.14.10"
+      },
+      "devDependencies": {
+        "@angular-devkit/build-angular": "^18.2.21",
+        "@angular/cli": "^18.2.21",
+        "@angular/compiler-cli": "^18.2.0",
+        "@types/jasmine": "~5.1.0",
+        "@types/mustache": "^4.2.6",
+        "jasmine-core": "~5.2.0",
+        "karma": "~6.4.0",
+        "karma-chrome-launcher": "~3.2.0",
+        "karma-coverage": "~2.2.0",
+        "karma-jasmine": "~5.1.0",
+        "karma-jasmine-html-reporter": "~2.1.0",
+        "typescript": "~5.5.2"
+      }
+    },
+    "node_modules/@ampproject/remapping": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@angular-devkit/architect": {
+      "version": "0.1802.21",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/architect/-/architect-0.1802.21.tgz",
+      "integrity": "sha512-+Ll+xtpKwZ3iLWN/YypvnCZV/F0MVbP+/7ZpMR+Xv/uB0OmribhBVj9WGaCd9I/bGgoYBw8wBV/NFNCKkf0k3Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "18.2.21",
+        "rxjs": "7.8.1"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular-devkit/architect/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@angular-devkit/build-angular": {
+      "version": "18.2.21",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/build-angular/-/build-angular-18.2.21.tgz",
+      "integrity": "sha512-0pJfURFpEUV2USgZ2TL3nNAaJmF9bICx9OVddBoC+F9FeOpVKxkcVIb+c8Km5zHFo1iyVtPZ6Rb25vFk9Zm/ug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "2.3.0",
+        "@angular-devkit/architect": "0.1802.21",
+        "@angular-devkit/build-webpack": "0.1802.21",
+        "@angular-devkit/core": "18.2.21",
+        "@angular/build": "18.2.21",
+        "@babel/core": "7.26.10",
+        "@babel/generator": "7.26.10",
+        "@babel/helper-annotate-as-pure": "7.25.9",
+        "@babel/helper-split-export-declaration": "7.24.7",
+        "@babel/plugin-transform-async-generator-functions": "7.26.8",
+        "@babel/plugin-transform-async-to-generator": "7.25.9",
+        "@babel/plugin-transform-runtime": "7.26.10",
+        "@babel/preset-env": "7.26.9",
+        "@babel/runtime": "7.26.10",
+        "@discoveryjs/json-ext": "0.6.1",
+        "@ngtools/webpack": "18.2.21",
+        "ansi-colors": "4.1.3",
+        "autoprefixer": "10.4.20",
+        "babel-loader": "9.1.3",
+        "browserslist": "^4.21.5",
+        "copy-webpack-plugin": "12.0.2",
+        "critters": "0.0.24",
+        "css-loader": "7.1.2",
+        "esbuild-wasm": "0.23.0",
+        "fast-glob": "3.3.2",
+        "http-proxy-middleware": "3.0.5",
+        "https-proxy-agent": "7.0.5",
+        "istanbul-lib-instrument": "6.0.3",
+        "jsonc-parser": "3.3.1",
+        "karma-source-map-support": "1.4.0",
+        "less": "4.2.0",
+        "less-loader": "12.2.0",
+        "license-webpack-plugin": "4.0.2",
+        "loader-utils": "3.3.1",
+        "magic-string": "0.30.11",
+        "mini-css-extract-plugin": "2.9.0",
+        "mrmime": "2.0.0",
+        "open": "10.1.0",
+        "ora": "5.4.1",
+        "parse5-html-rewriting-stream": "7.0.0",
+        "picomatch": "4.0.2",
+        "piscina": "4.6.1",
+        "postcss": "8.4.41",
+        "postcss-loader": "8.1.1",
+        "resolve-url-loader": "5.0.0",
+        "rxjs": "7.8.1",
+        "sass": "1.77.6",
+        "sass-loader": "16.0.0",
+        "semver": "7.6.3",
+        "source-map-loader": "5.0.0",
+        "source-map-support": "0.5.21",
+        "terser": "5.31.6",
+        "tree-kill": "1.2.2",
+        "tslib": "2.6.3",
+        "watchpack": "2.4.1",
+        "webpack": "5.94.0",
+        "webpack-dev-middleware": "7.4.2",
+        "webpack-dev-server": "5.2.2",
+        "webpack-merge": "6.0.1",
+        "webpack-subresource-integrity": "5.1.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "optionalDependencies": {
+        "esbuild": "0.23.0"
+      },
+      "peerDependencies": {
+        "@angular/compiler-cli": "^18.0.0",
+        "@angular/localize": "^18.0.0",
+        "@angular/platform-server": "^18.0.0",
+        "@angular/service-worker": "^18.0.0",
+        "@web/test-runner": "^0.18.0",
+        "browser-sync": "^3.0.2",
+        "jest": "^29.5.0",
+        "jest-environment-jsdom": "^29.5.0",
+        "karma": "^6.3.0",
+        "ng-packagr": "^18.0.0",
+        "protractor": "^7.0.0",
+        "tailwindcss": "^2.0.0 || ^3.0.0",
+        "typescript": ">=5.4 <5.6"
+      },
+      "peerDependenciesMeta": {
+        "@angular/localize": {
+          "optional": true
+        },
+        "@angular/platform-server": {
+          "optional": true
+        },
+        "@angular/service-worker": {
+          "optional": true
+        },
+        "@web/test-runner": {
+          "optional": true
+        },
+        "browser-sync": {
+          "optional": true
+        },
+        "jest": {
+          "optional": true
+        },
+        "jest-environment-jsdom": {
+          "optional": true
+        },
+        "karma": {
+          "optional": true
+        },
+        "ng-packagr": {
+          "optional": true
+        },
+        "protractor": {
+          "optional": true
+        },
+        "tailwindcss": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@angular-devkit/build-angular/node_modules/tslib": {
+      "version": "2.6.3",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.3.tgz",
+      "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
+      "dev": true,
+      "license": "0BSD"
+    },
+    "node_modules/@angular-devkit/build-webpack": {
+      "version": "0.1802.21",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/build-webpack/-/build-webpack-0.1802.21.tgz",
+      "integrity": "sha512-2jSVRhA3N4Elg8OLcBktgi+CMSjlAm/bBQJE6TQYbdQWnniuT7JAWUHA/iPf7MYlQE5qj4rnAni1CI/c1Bk4HQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/architect": "0.1802.21",
+        "rxjs": "7.8.1"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "webpack": "^5.30.0",
+        "webpack-dev-server": "^5.0.2"
+      }
+    },
+    "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@angular-devkit/core": {
+      "version": "18.2.21",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/core/-/core-18.2.21.tgz",
+      "integrity": "sha512-Lno6GNbJME85wpc/uqn+wamBxvfZJZFYSH8+oAkkyjU/hk8r5+X8DuyqsKAa0m8t46zSTUsonHsQhVe5vgrZeQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "8.17.1",
+        "ajv-formats": "3.0.1",
+        "jsonc-parser": "3.3.1",
+        "picomatch": "4.0.2",
+        "rxjs": "7.8.1",
+        "source-map": "0.7.4"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "chokidar": "^3.5.2"
+      },
+      "peerDependenciesMeta": {
+        "chokidar": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/core/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics": {
+      "version": "18.2.21",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/schematics/-/schematics-18.2.21.tgz",
+      "integrity": "sha512-yuC2vN4VL48JhnsaOa9J/o0Jl+cxOklRNQp5J2/ypMuRROaVCrZAPiX+ChSHh++kHYMpj8+ggNrrUwRNfMKACQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "18.2.21",
+        "jsonc-parser": "3.3.1",
+        "magic-string": "0.30.11",
+        "ora": "5.4.1",
+        "rxjs": "7.8.1"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@angular/animations": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/animations/-/animations-18.2.14.tgz",
+      "integrity": "sha512-Kp/MWShoYYO+R3lrrZbZgszbbLGVXHB+39mdJZwnIuZMDkeL3JsIBlSOzyJRTnpS1vITc+9jgHvP/6uKbMrW1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/core": "18.2.14"
+      }
+    },
+    "node_modules/@angular/build": {
+      "version": "18.2.21",
+      "resolved": "https://registry.npmmirror.com/@angular/build/-/build-18.2.21.tgz",
+      "integrity": "sha512-uvq3qP4cByJrUkV1ri0v3x6LxOFt4fDKiQdNwbQAqdxtfRs3ssEIoCGns4t89sTWXv6VZWBNDcDIKK9/Fa9mmg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "2.3.0",
+        "@angular-devkit/architect": "0.1802.21",
+        "@babel/core": "7.25.2",
+        "@babel/helper-annotate-as-pure": "7.24.7",
+        "@babel/helper-split-export-declaration": "7.24.7",
+        "@babel/plugin-syntax-import-attributes": "7.24.7",
+        "@inquirer/confirm": "3.1.22",
+        "@vitejs/plugin-basic-ssl": "1.1.0",
+        "browserslist": "^4.23.0",
+        "critters": "0.0.24",
+        "esbuild": "0.23.0",
+        "fast-glob": "3.3.2",
+        "https-proxy-agent": "7.0.5",
+        "listr2": "8.2.4",
+        "lmdb": "3.0.13",
+        "magic-string": "0.30.11",
+        "mrmime": "2.0.0",
+        "parse5-html-rewriting-stream": "7.0.0",
+        "picomatch": "4.0.2",
+        "piscina": "4.6.1",
+        "rollup": "4.22.4",
+        "sass": "1.77.6",
+        "semver": "7.6.3",
+        "vite": "~5.4.17",
+        "watchpack": "2.4.1"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "@angular/compiler-cli": "^18.0.0",
+        "@angular/localize": "^18.0.0",
+        "@angular/platform-server": "^18.0.0",
+        "@angular/service-worker": "^18.0.0",
+        "less": "^4.2.0",
+        "postcss": "^8.4.0",
+        "tailwindcss": "^2.0.0 || ^3.0.0",
+        "typescript": ">=5.4 <5.6"
+      },
+      "peerDependenciesMeta": {
+        "@angular/localize": {
+          "optional": true
+        },
+        "@angular/platform-server": {
+          "optional": true
+        },
+        "@angular/service-worker": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "postcss": {
+          "optional": true
+        },
+        "tailwindcss": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular/build/node_modules/@babel/core": {
+      "version": "7.25.2",
+      "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.25.2.tgz",
+      "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.24.7",
+        "@babel/generator": "^7.25.0",
+        "@babel/helper-compilation-targets": "^7.25.2",
+        "@babel/helper-module-transforms": "^7.25.2",
+        "@babel/helpers": "^7.25.0",
+        "@babel/parser": "^7.25.0",
+        "@babel/template": "^7.25.0",
+        "@babel/traverse": "^7.25.2",
+        "@babel/types": "^7.25.2",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@angular/build/node_modules/@babel/core/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@angular/build/node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.24.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz",
+      "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.24.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@angular/build/node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@angular/cli": {
+      "version": "18.2.21",
+      "resolved": "https://registry.npmmirror.com/@angular/cli/-/cli-18.2.21.tgz",
+      "integrity": "sha512-efweY4p8awRTbHs+HKdg6s44hl7Y0gdVlXYi3HeY8Z5JDC0abbka0K6sA/MrV9AXvn/5ovxYbxiL3AsOApjTpg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/architect": "0.1802.21",
+        "@angular-devkit/core": "18.2.21",
+        "@angular-devkit/schematics": "18.2.21",
+        "@inquirer/prompts": "5.3.8",
+        "@listr2/prompt-adapter-inquirer": "2.0.15",
+        "@schematics/angular": "18.2.21",
+        "@yarnpkg/lockfile": "1.1.0",
+        "ini": "4.1.3",
+        "jsonc-parser": "3.3.1",
+        "listr2": "8.2.4",
+        "npm-package-arg": "11.0.3",
+        "npm-pick-manifest": "9.1.0",
+        "pacote": "18.0.6",
+        "resolve": "1.22.8",
+        "semver": "7.6.3",
+        "symbol-observable": "4.0.0",
+        "yargs": "17.7.2"
+      },
+      "bin": {
+        "ng": "bin/ng.js"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular/common": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/common/-/common-18.2.14.tgz",
+      "integrity": "sha512-ZPRswzaVRiqcfZoowuAM22Hr2/z10ajWOUoFDoQ9tWqz/fH/773kJv2F9VvePIekgNPCzaizqv9gF6tGNqaAwg==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/core": "18.2.14",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
+    "node_modules/@angular/compiler": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/compiler/-/compiler-18.2.14.tgz",
+      "integrity": "sha512-Mpq3v/mztQzGAQAAFV+wAI1hlXxZ0m8eDBgaN2kD3Ue+r4S6bLm1Vlryw0iyUnt05PcFIdxPT6xkcphq5pl6lw==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/core": "18.2.14"
+      },
+      "peerDependenciesMeta": {
+        "@angular/core": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular/compiler-cli": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/compiler-cli/-/compiler-cli-18.2.14.tgz",
+      "integrity": "sha512-BmmjyrFSBSYkm0tBSqpu4cwnJX/b/XvhM36mj2k8jah3tNS5zLDDx5w6tyHmaPJa/1D95MlXx2h6u7K9D+Mhew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "7.25.2",
+        "@jridgewell/sourcemap-codec": "^1.4.14",
+        "chokidar": "^4.0.0",
+        "convert-source-map": "^1.5.1",
+        "reflect-metadata": "^0.2.0",
+        "semver": "^7.0.0",
+        "tslib": "^2.3.0",
+        "yargs": "^17.2.1"
+      },
+      "bin": {
+        "ng-xi18n": "bundles/src/bin/ng_xi18n.js",
+        "ngc": "bundles/src/bin/ngc.js",
+        "ngcc": "bundles/ngcc/index.js"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/compiler": "18.2.14",
+        "typescript": ">=5.4 <5.6"
+      }
+    },
+    "node_modules/@angular/compiler-cli/node_modules/@babel/core": {
+      "version": "7.25.2",
+      "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.25.2.tgz",
+      "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.24.7",
+        "@babel/generator": "^7.25.0",
+        "@babel/helper-compilation-targets": "^7.25.2",
+        "@babel/helper-module-transforms": "^7.25.2",
+        "@babel/helpers": "^7.25.0",
+        "@babel/parser": "^7.25.0",
+        "@babel/template": "^7.25.0",
+        "@babel/traverse": "^7.25.2",
+        "@babel/types": "^7.25.2",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@angular/compiler-cli/node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@angular/compiler-cli/node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@angular/core": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/core/-/core-18.2.14.tgz",
+      "integrity": "sha512-BIPrCs93ZZTY9ym7yfoTgAQ5rs706yoYeAdrgc8kh/bDbM9DawxKlgeKBx2FLt09Y0YQ1bFhKVp0cV4gDEaMxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "rxjs": "^6.5.3 || ^7.4.0",
+        "zone.js": "~0.14.10"
+      }
+    },
+    "node_modules/@angular/forms": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/forms/-/forms-18.2.14.tgz",
+      "integrity": "sha512-fZVwXctmBJa5VdopJae/T9MYKPXNd04+6j4k/6X819y+9fiyWLJt2QicSc5Rc+YD9mmhXag3xaljlrnotf9VGA==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/common": "18.2.14",
+        "@angular/core": "18.2.14",
+        "@angular/platform-browser": "18.2.14",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
+    "node_modules/@angular/platform-browser": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/platform-browser/-/platform-browser-18.2.14.tgz",
+      "integrity": "sha512-W+JTxI25su3RiZVZT3Yrw6KNUCmOIy7OZIZ+612skPgYK2f2qil7VclnW1oCwG896h50cMJU/lnAfxZxefQgyQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/animations": "18.2.14",
+        "@angular/common": "18.2.14",
+        "@angular/core": "18.2.14"
+      },
+      "peerDependenciesMeta": {
+        "@angular/animations": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular/platform-browser-dynamic": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.14.tgz",
+      "integrity": "sha512-QOv+o89u8HLN0LG8faTIVHKBxfkOBHVDB0UuXy19+HJofWZGGvho+vGjV0/IAkhZnMC4Sxdoy/mOHP2ytALX3A==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/common": "18.2.14",
+        "@angular/compiler": "18.2.14",
+        "@angular/core": "18.2.14",
+        "@angular/platform-browser": "18.2.14"
+      }
+    },
+    "node_modules/@angular/router": {
+      "version": "18.2.14",
+      "resolved": "https://registry.npmmirror.com/@angular/router/-/router-18.2.14.tgz",
+      "integrity": "sha512-v/gweh8MBjjDfh1QssuyjISa+6SVVIvIZox7MaMs81RkaoVHwS9grDtPud1pTKHzms2KxSVpvwwyvkRJQplueg==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
+      },
+      "peerDependencies": {
+        "@angular/common": "18.2.14",
+        "@angular/core": "18.2.14",
+        "@angular/platform-browser": "18.2.14",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.7.tgz",
+      "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.7.tgz",
+      "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.26.10",
+      "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.26.10.tgz",
+      "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.26.2",
+        "@babel/generator": "^7.26.10",
+        "@babel/helper-compilation-targets": "^7.26.5",
+        "@babel/helper-module-transforms": "^7.26.0",
+        "@babel/helpers": "^7.26.10",
+        "@babel/parser": "^7.26.10",
+        "@babel/template": "^7.26.9",
+        "@babel/traverse": "^7.26.10",
+        "@babel/types": "^7.26.10",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/core/node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@babel/core/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.26.10",
+      "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.26.10.tgz",
+      "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.26.10",
+        "@babel/types": "^7.26.10",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
+      "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+      "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/compat-data": "^7.29.7",
+        "@babel/helper-validator-option": "^7.29.7",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-create-class-features-plugin": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz",
+      "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.29.7",
+        "@babel/helper-member-expression-to-functions": "^7.29.7",
+        "@babel/helper-optimise-call-expression": "^7.29.7",
+        "@babel/helper-replace-supers": "^7.29.7",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
+        "@babel/traverse": "^7.29.7",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz",
+      "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-create-regexp-features-plugin": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz",
+      "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.29.7",
+        "regexpu-core": "^6.3.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz",
+      "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-define-polyfill-provider": {
+      "version": "0.6.8",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
+      "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-compilation-targets": "^7.28.6",
+        "@babel/helper-plugin-utils": "^7.28.6",
+        "debug": "^4.4.3",
+        "lodash.debounce": "^4.0.8",
+        "resolve": "^1.22.11"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+      }
+    },
+    "node_modules/@babel/helper-define-polyfill-provider/node_modules/resolve": {
+      "version": "1.22.12",
+      "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz",
+      "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "is-core-module": "^2.16.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/@babel/helper-globals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+      "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-member-expression-to-functions": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz",
+      "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+      "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+      "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-optimise-call-expression": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz",
+      "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-plugin-utils": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+      "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-remap-async-to-generator": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz",
+      "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.29.7",
+        "@babel/helper-wrap-function": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz",
+      "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-replace-supers": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz",
+      "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-member-expression-to-functions": "^7.29.7",
+        "@babel/helper-optimise-call-expression": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz",
+      "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-split-export-declaration": {
+      "version": "7.24.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
+      "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.24.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "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==",
+      "dev": true,
+      "license": "MIT",
+      "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==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+      "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-wrap-function": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz",
+      "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/template": "^7.29.7",
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz",
+      "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "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==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.8"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz",
+      "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz",
+      "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz",
+      "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz",
+      "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
+        "@babel/plugin-transform-optional-chaining": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.13.0"
+      }
+    },
+    "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz",
+      "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-proposal-private-property-in-object": {
+      "version": "7.21.0-placeholder-for-preset-env.2",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+      "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-import-assertions": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz",
+      "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-import-attributes": {
+      "version": "7.24.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz",
+      "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.24.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+      "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+        "@babel/helper-plugin-utils": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-arrow-functions": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz",
+      "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-async-generator-functions": {
+      "version": "7.26.8",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz",
+      "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.26.5",
+        "@babel/helper-remap-async-to-generator": "^7.25.9",
+        "@babel/traverse": "^7.26.8"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-async-to-generator": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz",
+      "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.25.9",
+        "@babel/helper-plugin-utils": "^7.25.9",
+        "@babel/helper-remap-async-to-generator": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-block-scoped-functions": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz",
+      "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-block-scoping": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz",
+      "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-class-properties": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz",
+      "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-class-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-class-static-block": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz",
+      "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-class-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.12.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-classes": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz",
+      "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.29.7",
+        "@babel/helper-compilation-targets": "^7.29.7",
+        "@babel/helper-globals": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/helper-replace-supers": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz",
+      "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-computed-properties": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz",
+      "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/template": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-destructuring": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz",
+      "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-dotall-regex": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz",
+      "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-duplicate-keys": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz",
+      "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz",
+      "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-dynamic-import": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz",
+      "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-exponentiation-operator": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz",
+      "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-export-namespace-from": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz",
+      "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-for-of": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz",
+      "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-function-name": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz",
+      "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-compilation-targets": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-json-strings": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz",
+      "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-literals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz",
+      "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz",
+      "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-member-expression-literals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz",
+      "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-modules-amd": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz",
+      "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-transforms": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-modules-commonjs": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz",
+      "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-transforms": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-modules-systemjs": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.8.tgz",
+      "integrity": "sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-transforms": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "@babel/traverse": "^7.29.8"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-modules-umd": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz",
+      "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-transforms": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz",
+      "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-new-target": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz",
+      "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz",
+      "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-numeric-separator": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz",
+      "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-object-rest-spread": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz",
+      "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-compilation-targets": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/plugin-transform-destructuring": "^7.29.7",
+        "@babel/plugin-transform-parameters": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-object-super": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz",
+      "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/helper-replace-supers": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-optional-catch-binding": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz",
+      "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-optional-chaining": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz",
+      "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-parameters": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz",
+      "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-private-methods": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz",
+      "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-class-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-private-property-in-object": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz",
+      "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.29.7",
+        "@babel/helper-create-class-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz",
+      "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-property-literals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz",
+      "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-regenerator": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.8.tgz",
+      "integrity": "sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-regexp-modifiers": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz",
+      "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-reserved-words": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz",
+      "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-runtime": {
+      "version": "7.26.10",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz",
+      "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.25.9",
+        "@babel/helper-plugin-utils": "^7.26.5",
+        "babel-plugin-polyfill-corejs2": "^0.4.10",
+        "babel-plugin-polyfill-corejs3": "^0.11.0",
+        "babel-plugin-polyfill-regenerator": "^0.6.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/plugin-transform-shorthand-properties": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz",
+      "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-spread": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz",
+      "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-sticky-regex": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz",
+      "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-template-literals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz",
+      "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-typeof-symbol": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz",
+      "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-unicode-escapes": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz",
+      "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-unicode-property-regex": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz",
+      "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-unicode-regex": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz",
+      "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz",
+      "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/preset-env": {
+      "version": "7.26.9",
+      "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.26.9.tgz",
+      "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/compat-data": "^7.26.8",
+        "@babel/helper-compilation-targets": "^7.26.5",
+        "@babel/helper-plugin-utils": "^7.26.5",
+        "@babel/helper-validator-option": "^7.25.9",
+        "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9",
+        "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9",
+        "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9",
+        "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9",
+        "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9",
+        "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+        "@babel/plugin-syntax-import-assertions": "^7.26.0",
+        "@babel/plugin-syntax-import-attributes": "^7.26.0",
+        "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+        "@babel/plugin-transform-arrow-functions": "^7.25.9",
+        "@babel/plugin-transform-async-generator-functions": "^7.26.8",
+        "@babel/plugin-transform-async-to-generator": "^7.25.9",
+        "@babel/plugin-transform-block-scoped-functions": "^7.26.5",
+        "@babel/plugin-transform-block-scoping": "^7.25.9",
+        "@babel/plugin-transform-class-properties": "^7.25.9",
+        "@babel/plugin-transform-class-static-block": "^7.26.0",
+        "@babel/plugin-transform-classes": "^7.25.9",
+        "@babel/plugin-transform-computed-properties": "^7.25.9",
+        "@babel/plugin-transform-destructuring": "^7.25.9",
+        "@babel/plugin-transform-dotall-regex": "^7.25.9",
+        "@babel/plugin-transform-duplicate-keys": "^7.25.9",
+        "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9",
+        "@babel/plugin-transform-dynamic-import": "^7.25.9",
+        "@babel/plugin-transform-exponentiation-operator": "^7.26.3",
+        "@babel/plugin-transform-export-namespace-from": "^7.25.9",
+        "@babel/plugin-transform-for-of": "^7.26.9",
+        "@babel/plugin-transform-function-name": "^7.25.9",
+        "@babel/plugin-transform-json-strings": "^7.25.9",
+        "@babel/plugin-transform-literals": "^7.25.9",
+        "@babel/plugin-transform-logical-assignment-operators": "^7.25.9",
+        "@babel/plugin-transform-member-expression-literals": "^7.25.9",
+        "@babel/plugin-transform-modules-amd": "^7.25.9",
+        "@babel/plugin-transform-modules-commonjs": "^7.26.3",
+        "@babel/plugin-transform-modules-systemjs": "^7.25.9",
+        "@babel/plugin-transform-modules-umd": "^7.25.9",
+        "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9",
+        "@babel/plugin-transform-new-target": "^7.25.9",
+        "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6",
+        "@babel/plugin-transform-numeric-separator": "^7.25.9",
+        "@babel/plugin-transform-object-rest-spread": "^7.25.9",
+        "@babel/plugin-transform-object-super": "^7.25.9",
+        "@babel/plugin-transform-optional-catch-binding": "^7.25.9",
+        "@babel/plugin-transform-optional-chaining": "^7.25.9",
+        "@babel/plugin-transform-parameters": "^7.25.9",
+        "@babel/plugin-transform-private-methods": "^7.25.9",
+        "@babel/plugin-transform-private-property-in-object": "^7.25.9",
+        "@babel/plugin-transform-property-literals": "^7.25.9",
+        "@babel/plugin-transform-regenerator": "^7.25.9",
+        "@babel/plugin-transform-regexp-modifiers": "^7.26.0",
+        "@babel/plugin-transform-reserved-words": "^7.25.9",
+        "@babel/plugin-transform-shorthand-properties": "^7.25.9",
+        "@babel/plugin-transform-spread": "^7.25.9",
+        "@babel/plugin-transform-sticky-regex": "^7.25.9",
+        "@babel/plugin-transform-template-literals": "^7.26.8",
+        "@babel/plugin-transform-typeof-symbol": "^7.26.7",
+        "@babel/plugin-transform-unicode-escapes": "^7.25.9",
+        "@babel/plugin-transform-unicode-property-regex": "^7.25.9",
+        "@babel/plugin-transform-unicode-regex": "^7.25.9",
+        "@babel/plugin-transform-unicode-sets-regex": "^7.25.9",
+        "@babel/preset-modules": "0.1.6-no-external-plugins",
+        "babel-plugin-polyfill-corejs2": "^0.4.10",
+        "babel-plugin-polyfill-corejs3": "^0.11.0",
+        "babel-plugin-polyfill-regenerator": "^0.6.1",
+        "core-js-compat": "^3.40.0",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/preset-env/node_modules/@babel/plugin-syntax-import-attributes": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz",
+      "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/preset-env/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/preset-modules": {
+      "version": "0.1.6-no-external-plugins",
+      "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+      "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@babel/types": "^7.4.4",
+        "esutils": "^2.0.2"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.26.10",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.26.10.tgz",
+      "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz",
+      "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.8.tgz",
+      "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.8",
+        "@babel/helper-globals": "^7.29.7",
+        "@babel/parser": "^7.29.8",
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.8",
+        "debug": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse/node_modules/@babel/generator": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.8.tgz",
+      "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.29.8",
+        "@babel/types": "^7.29.8",
+        "@jridgewell/gen-mapping": "^0.3.12",
+        "@jridgewell/trace-mapping": "^0.3.28",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.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==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@colors/colors": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/@colors/colors/-/colors-1.5.0.tgz",
+      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/@discoveryjs/json-ext": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz",
+      "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.17.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz",
+      "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.23.0.tgz",
+      "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz",
+      "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.23.0.tgz",
+      "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz",
+      "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz",
+      "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz",
+      "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz",
+      "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz",
+      "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz",
+      "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz",
+      "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz",
+      "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz",
+      "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz",
+      "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz",
+      "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz",
+      "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz",
+      "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz",
+      "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz",
+      "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz",
+      "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz",
+      "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz",
+      "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz",
+      "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz",
+      "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/checkbox": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/checkbox/-/checkbox-2.5.0.tgz",
+      "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/figures": "^1.0.5",
+        "@inquirer/type": "^1.5.3",
+        "ansi-escapes": "^4.3.2",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/confirm": {
+      "version": "3.1.22",
+      "resolved": "https://registry.npmmirror.com/@inquirer/confirm/-/confirm-3.1.22.tgz",
+      "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.0.10",
+        "@inquirer/type": "^1.5.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/core": {
+      "version": "9.2.1",
+      "resolved": "https://registry.npmmirror.com/@inquirer/core/-/core-9.2.1.tgz",
+      "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/figures": "^1.0.6",
+        "@inquirer/type": "^2.0.0",
+        "@types/mute-stream": "^0.0.4",
+        "@types/node": "^22.5.5",
+        "@types/wrap-ansi": "^3.0.0",
+        "ansi-escapes": "^4.3.2",
+        "cli-width": "^4.1.0",
+        "mute-stream": "^1.0.0",
+        "signal-exit": "^4.1.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^6.2.0",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/core/node_modules/@inquirer/type": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/type/-/type-2.0.0.tgz",
+      "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mute-stream": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/editor": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/editor/-/editor-2.2.0.tgz",
+      "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/type": "^1.5.3",
+        "external-editor": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/expand": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/expand/-/expand-2.3.0.tgz",
+      "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/type": "^1.5.3",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/figures": {
+      "version": "1.0.15",
+      "resolved": "https://registry.npmmirror.com/@inquirer/figures/-/figures-1.0.15.tgz",
+      "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/input": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/input/-/input-2.3.0.tgz",
+      "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/type": "^1.5.3"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/number": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/number/-/number-1.1.0.tgz",
+      "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/type": "^1.5.3"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/password": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/password/-/password-2.2.0.tgz",
+      "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/type": "^1.5.3",
+        "ansi-escapes": "^4.3.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/prompts": {
+      "version": "5.3.8",
+      "resolved": "https://registry.npmmirror.com/@inquirer/prompts/-/prompts-5.3.8.tgz",
+      "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/checkbox": "^2.4.7",
+        "@inquirer/confirm": "^3.1.22",
+        "@inquirer/editor": "^2.1.22",
+        "@inquirer/expand": "^2.1.22",
+        "@inquirer/input": "^2.2.9",
+        "@inquirer/number": "^1.0.10",
+        "@inquirer/password": "^2.1.22",
+        "@inquirer/rawlist": "^2.2.4",
+        "@inquirer/search": "^1.0.7",
+        "@inquirer/select": "^2.4.7"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/rawlist": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/rawlist/-/rawlist-2.3.0.tgz",
+      "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/type": "^1.5.3",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/search": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/search/-/search-1.1.0.tgz",
+      "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/figures": "^1.0.5",
+        "@inquirer/type": "^1.5.3",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/select": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/@inquirer/select/-/select-2.5.0.tgz",
+      "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^9.1.0",
+        "@inquirer/figures": "^1.0.5",
+        "@inquirer/type": "^1.5.3",
+        "ansi-escapes": "^4.3.2",
+        "yoctocolors-cjs": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/type": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmmirror.com/@inquirer/type/-/type-1.5.5.tgz",
+      "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mute-stream": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@isaacs/cliui/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz",
+      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@istanbuljs/schema": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.6.tgz",
+      "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.11",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+      "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@jsonjoy.com/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/buffers": {
+      "version": "17.67.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz",
+      "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/codegen": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz",
+      "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-core": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-core/-/fs-core-4.64.0.tgz",
+      "integrity": "sha512-zs2TAq7Six5jgMuoMNjpspAvOP3mhtgq/k1UyQodEzCtQi/N83y2/y+zcvnZSGp/Rxq96DBN+bValOBQAyn/ew==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/fs-node-builtins": "4.64.0",
+        "@jsonjoy.com/fs-node-utils": "4.64.0",
+        "thingies": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-fsa": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-fsa/-/fs-fsa-4.64.0.tgz",
+      "integrity": "sha512-nMWOVbkLFyEgmXZih3wyvxA9XpgyyqyfrINMHvEFqhi7uqfRl7c9ERJt6yX7vgMPrB9Uo+OJO+Spa0cFzPD01w==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/fs-core": "4.64.0",
+        "@jsonjoy.com/fs-node-builtins": "4.64.0",
+        "@jsonjoy.com/fs-node-utils": "4.64.0",
+        "thingies": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-node": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-node/-/fs-node-4.64.0.tgz",
+      "integrity": "sha512-dO+NNkODbUli4uV42bcNrrLvq5rE7SNpdZ5TNd0dtbLsAaNK3MDiIC9lUi+brboGoIjW6vd2fB1qao60nrk5xA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/fs-core": "4.64.0",
+        "@jsonjoy.com/fs-node-builtins": "4.64.0",
+        "@jsonjoy.com/fs-node-utils": "4.64.0",
+        "@jsonjoy.com/fs-print": "4.64.0",
+        "@jsonjoy.com/fs-snapshot": "4.64.0",
+        "glob-to-regex.js": "^1.0.0",
+        "thingies": "^2.5.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-node-builtins": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.64.0.tgz",
+      "integrity": "sha512-/o7WRFhUWaM/fOrslwLZGnzn4RmRILykn+lAL+mNObqqRNw+CQSiij6hpCeZ+C7buhdoVo7go/OYqzaSUfDYmA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-node-to-fsa": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.64.0.tgz",
+      "integrity": "sha512-WDD9WVs0hb7UAEKTgZW2f66WDrbj7gIIWwpP3spbLyXa0rghtUaFTB8L4gdR3ZCWwiKIsj38/CNijpVmpnuPUw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/fs-fsa": "4.64.0",
+        "@jsonjoy.com/fs-node-builtins": "4.64.0",
+        "@jsonjoy.com/fs-node-utils": "4.64.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-node-utils": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.64.0.tgz",
+      "integrity": "sha512-k5Indsx9hWW9xSF7Y6oSKKwtCUNhzZxadub3owhIlitc+iMRVlPPdX2duTKQWBL3qNWpXya8jykgaaWpheeS4w==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/fs-node-builtins": "4.64.0",
+        "glob-to-regex.js": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-print": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-print/-/fs-print-4.64.0.tgz",
+      "integrity": "sha512-PHZFccchvkhWrwPWHjmVAhbC3vSHCtyZvlZfJJ3ho2bnzl450hXri6/8e6pbkWdH+SkmLXNml0sV8e5HDAfxKw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/fs-node-utils": "4.64.0",
+        "tree-dump": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-snapshot": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.64.0.tgz",
+      "integrity": "sha512-oM7UDeL83q6NBzzsfKAsYKXKVXlykKFqqOLh4xZZKAzzROTlInkPbc6LTDGThEOnPiFiUzA7tYziHG9xavd76Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/buffers": "^17.65.0",
+        "@jsonjoy.com/fs-node-utils": "4.64.0",
+        "@jsonjoy.com/json-pack": "^17.65.0",
+        "@jsonjoy.com/util": "^17.65.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": {
+      "version": "17.67.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/base64/-/base64-17.67.0.tgz",
+      "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": {
+      "version": "17.67.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz",
+      "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": {
+      "version": "17.67.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz",
+      "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/base64": "17.67.0",
+        "@jsonjoy.com/buffers": "17.67.0",
+        "@jsonjoy.com/codegen": "17.67.0",
+        "@jsonjoy.com/json-pointer": "17.67.0",
+        "@jsonjoy.com/util": "17.67.0",
+        "hyperdyperid": "^1.2.0",
+        "thingies": "^2.5.0",
+        "tree-dump": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": {
+      "version": "17.67.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz",
+      "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/util": "17.67.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": {
+      "version": "17.67.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/util/-/util-17.67.0.tgz",
+      "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/buffers": "17.67.0",
+        "@jsonjoy.com/codegen": "17.67.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/json-pack": {
+      "version": "1.21.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz",
+      "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/base64": "^1.1.2",
+        "@jsonjoy.com/buffers": "^1.2.0",
+        "@jsonjoy.com/codegen": "^1.0.0",
+        "@jsonjoy.com/json-pointer": "^1.0.2",
+        "@jsonjoy.com/util": "^1.9.0",
+        "hyperdyperid": "^1.2.0",
+        "thingies": "^2.5.0",
+        "tree-dump": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz",
+      "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/json-pointer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz",
+      "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/codegen": "^1.0.0",
+        "@jsonjoy.com/util": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/util": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/util/-/util-1.9.0.tgz",
+      "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/buffers": "^1.0.0",
+        "@jsonjoy.com/codegen": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz",
+      "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/@leichtgewicht/ip-codec": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
+      "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@listr2/prompt-adapter-inquirer": {
+      "version": "2.0.15",
+      "resolved": "https://registry.npmmirror.com/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz",
+      "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/type": "^1.5.1"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "peerDependencies": {
+        "@inquirer/prompts": ">= 3 < 6"
+      }
+    },
+    "node_modules/@lmdb/lmdb-darwin-arm64": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmmirror.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz",
+      "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-darwin-x64": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmmirror.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.13.tgz",
+      "integrity": "sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmmirror.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.13.tgz",
+      "integrity": "sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm64": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmmirror.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.13.tgz",
+      "integrity": "sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-x64": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmmirror.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.13.tgz",
+      "integrity": "sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-win32-x64": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmmirror.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz",
+      "integrity": "sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz",
+      "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz",
+      "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz",
+      "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz",
+      "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz",
+      "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz",
+      "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@ngtools/webpack": {
+      "version": "18.2.21",
+      "resolved": "https://registry.npmmirror.com/@ngtools/webpack/-/webpack-18.2.21.tgz",
+      "integrity": "sha512-mfLT7lXbyJRlsazuPyuF5AGsMcgzRJRwsDlgxFbiy1DBlaF1chRFsXrKYj1gQ/WXQWNcEd11aedU0Rt+iCNDVw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "@angular/compiler-cli": "^18.0.0",
+        "typescript": ">=5.4 <5.6",
+        "webpack": "^5.54.0"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@npmcli/agent": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/@npmcli/agent/-/agent-2.2.2.tgz",
+      "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "http-proxy-agent": "^7.0.0",
+        "https-proxy-agent": "^7.0.1",
+        "lru-cache": "^10.0.1",
+        "socks-proxy-agent": "^8.0.3"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/agent/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@npmcli/fs": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/@npmcli/fs/-/fs-3.1.1.tgz",
+      "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/git": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmmirror.com/@npmcli/git/-/git-5.0.8.tgz",
+      "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/promise-spawn": "^7.0.0",
+        "ini": "^4.1.3",
+        "lru-cache": "^10.0.1",
+        "npm-pick-manifest": "^9.0.0",
+        "proc-log": "^4.0.0",
+        "promise-inflight": "^1.0.1",
+        "promise-retry": "^2.0.1",
+        "semver": "^7.3.5",
+        "which": "^4.0.0"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/git/node_modules/isexe": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.5.tgz",
+      "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@npmcli/git/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/@npmcli/git/node_modules/which": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/which/-/which-4.0.0.tgz",
+      "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^3.1.1"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/installed-package-contents": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz",
+      "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "npm-bundled": "^3.0.0",
+        "npm-normalize-package-bin": "^3.0.0"
+      },
+      "bin": {
+        "installed-package-contents": "bin/index.js"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/node-gyp": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz",
+      "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/package-json": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/@npmcli/package-json/-/package-json-5.2.1.tgz",
+      "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/git": "^5.0.0",
+        "glob": "^10.2.2",
+        "hosted-git-info": "^7.0.0",
+        "json-parse-even-better-errors": "^3.0.0",
+        "normalize-package-data": "^6.0.0",
+        "proc-log": "^4.0.0",
+        "semver": "^7.5.3"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/package-json/node_modules/brace-expansion": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz",
+      "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@npmcli/package-json/node_modules/glob": {
+      "version": "10.4.5",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz",
+      "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@npmcli/package-json/node_modules/minimatch": {
+      "version": "9.0.9",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
+      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@npmcli/promise-spawn": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmmirror.com/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz",
+      "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "which": "^4.0.0"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/promise-spawn/node_modules/isexe": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.5.tgz",
+      "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@npmcli/promise-spawn/node_modules/which": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/which/-/which-4.0.0.tgz",
+      "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^3.1.1"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/redact": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/@npmcli/redact/-/redact-2.0.1.tgz",
+      "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/run-script": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/@npmcli/run-script/-/run-script-8.1.0.tgz",
+      "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/node-gyp": "^3.0.0",
+        "@npmcli/package-json": "^5.0.0",
+        "@npmcli/promise-spawn": "^7.0.0",
+        "node-gyp": "^10.0.0",
+        "proc-log": "^4.0.0",
+        "which": "^4.0.0"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@npmcli/run-script/node_modules/isexe": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.5.tgz",
+      "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@npmcli/run-script/node_modules/which": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/which/-/which-4.0.0.tgz",
+      "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^3.1.1"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
+      "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
+      "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
+      "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
+      "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
+      "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
+      "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
+      "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
+      "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
+      "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
+      "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
+      "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
+      "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
+      "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
+      "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
+      "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
+      "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@schematics/angular": {
+      "version": "18.2.21",
+      "resolved": "https://registry.npmmirror.com/@schematics/angular/-/angular-18.2.21.tgz",
+      "integrity": "sha512-5Ai+NEflQZi67y4NsQ3o04iEp7zT0/BUFVCrJ3CueU3uYQGs8jrN1Lk6tvQ9c5HzGcTDrMXuTrCswyR9o6ecpA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "18.2.21",
+        "@angular-devkit/schematics": "18.2.21",
+        "jsonc-parser": "3.3.1"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@sigstore/bundle": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/@sigstore/bundle/-/bundle-2.3.2.tgz",
+      "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@sigstore/protobuf-specs": "^0.3.2"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@sigstore/core": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/@sigstore/core/-/core-1.1.0.tgz",
+      "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@sigstore/protobuf-specs": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmmirror.com/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz",
+      "integrity": "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^18.17.0 || >=20.5.0"
+      }
+    },
+    "node_modules/@sigstore/sign": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/@sigstore/sign/-/sign-2.3.2.tgz",
+      "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@sigstore/bundle": "^2.3.2",
+        "@sigstore/core": "^1.0.0",
+        "@sigstore/protobuf-specs": "^0.3.2",
+        "make-fetch-happen": "^13.0.1",
+        "proc-log": "^4.2.0",
+        "promise-retry": "^2.0.1"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@sigstore/tuf": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmmirror.com/@sigstore/tuf/-/tuf-2.3.4.tgz",
+      "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@sigstore/protobuf-specs": "^0.3.2",
+        "tuf-js": "^2.2.1"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@sigstore/verify": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@sigstore/verify/-/verify-1.2.1.tgz",
+      "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@sigstore/bundle": "^2.3.2",
+        "@sigstore/core": "^1.1.0",
+        "@sigstore/protobuf-specs": "^0.3.2"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@sindresorhus/merge-streams": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
+      "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@tufjs/canonical-json": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
+      "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@tufjs/models": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/@tufjs/models/-/models-2.0.1.tgz",
+      "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@tufjs/canonical-json": "2.0.0",
+        "minimatch": "^9.0.4"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/@tufjs/models/node_modules/brace-expansion": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz",
+      "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/@tufjs/models/node_modules/minimatch": {
+      "version": "9.0.9",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
+      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.6",
+      "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz",
+      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/bonjour": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@types/bonjour/-/bonjour-3.5.13.tgz",
+      "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/connect-history-api-fallback": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmmirror.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz",
+      "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/express-serve-static-core": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/cors": {
+      "version": "2.8.19",
+      "resolved": "https://registry.npmmirror.com/@types/cors/-/cors-2.8.19.tgz",
+      "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/express": {
+      "version": "4.17.25",
+      "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.25.tgz",
+      "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^4.17.33",
+        "@types/qs": "*",
+        "@types/serve-static": "^1"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "4.19.9",
+      "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz",
+      "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.5.tgz",
+      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/http-proxy": {
+      "version": "1.17.17",
+      "resolved": "https://registry.npmmirror.com/@types/http-proxy/-/http-proxy-1.17.17.tgz",
+      "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/jasmine": {
+      "version": "5.1.15",
+      "resolved": "https://registry.npmmirror.com/@types/jasmine/-/jasmine-5.1.15.tgz",
+      "integrity": "sha512-ZAC8KjmV2MJxbNTrwXFN+HKeajpXQZp6KpPiR6Aa4XvaEnjP6qh23lL/Rqb7AYzlp3h/rcwDrQ7Gg7q28cQTQg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/mime": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
+      "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/mustache": {
+      "version": "4.2.6",
+      "resolved": "https://registry.npmmirror.com/@types/mustache/-/mustache-4.2.6.tgz",
+      "integrity": "sha512-t+8/QWTAhOFlrF1IVZqKnMRJi84EgkIK5Kh0p2JV4OLywUvCwJPFxbJAl7XAow7DVIHsF+xW9f1MVzg0L6Szjw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/mute-stream": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/mute-stream/-/mute-stream-0.0.4.tgz",
+      "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "22.20.1",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.20.1.tgz",
+      "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.21.0"
+      }
+    },
+    "node_modules/@types/node-forge": {
+      "version": "1.3.14",
+      "resolved": "https://registry.npmmirror.com/@types/node-forge/-/node-forge-1.3.14.tgz",
+      "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/pako": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/pako/-/pako-2.0.4.tgz",
+      "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/qs": {
+      "version": "6.15.1",
+      "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.15.1.tgz",
+      "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/raf": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz",
+      "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz",
+      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/retry": {
+      "version": "0.12.2",
+      "resolved": "https://registry.npmmirror.com/@types/retry/-/retry-0.12.2.tgz",
+      "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/send": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@types/send/-/send-1.2.1.tgz",
+      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-index": {
+      "version": "1.9.4",
+      "resolved": "https://registry.npmmirror.com/@types/serve-index/-/serve-index-1.9.4.tgz",
+      "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "1.15.10",
+      "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.10.tgz",
+      "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*",
+        "@types/send": "<1"
+      }
+    },
+    "node_modules/@types/serve-static/node_modules/@types/send": {
+      "version": "0.17.6",
+      "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.6.tgz",
+      "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/mime": "^1",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/sockjs": {
+      "version": "0.3.36",
+      "resolved": "https://registry.npmmirror.com/@types/sockjs/-/sockjs-0.3.36.tgz",
+      "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/trusted-types": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+      "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@types/wrap-ansi": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz",
+      "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/ws": {
+      "version": "8.18.1",
+      "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
+      "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@vitejs/plugin-basic-ssl": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz",
+      "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.6.0"
+      },
+      "peerDependencies": {
+        "vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/@webassemblyjs/ast": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+      "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/helper-numbers": "1.13.2",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+      "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-api-error": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+      "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-buffer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+      "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-numbers": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+      "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+        "@webassemblyjs/helper-api-error": "1.13.2",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+      "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-wasm-section": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+      "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/wasm-gen": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/ieee754": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+      "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "node_modules/@webassemblyjs/leb128": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+      "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/utf8": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+      "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/wasm-edit": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+      "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/helper-wasm-section": "1.14.1",
+        "@webassemblyjs/wasm-gen": "1.14.1",
+        "@webassemblyjs/wasm-opt": "1.14.1",
+        "@webassemblyjs/wasm-parser": "1.14.1",
+        "@webassemblyjs/wast-printer": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-gen": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+      "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/ieee754": "1.13.2",
+        "@webassemblyjs/leb128": "1.13.2",
+        "@webassemblyjs/utf8": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-opt": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+      "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/wasm-gen": "1.14.1",
+        "@webassemblyjs/wasm-parser": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-parser": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+      "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-api-error": "1.13.2",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/ieee754": "1.13.2",
+        "@webassemblyjs/leb128": "1.13.2",
+        "@webassemblyjs/utf8": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/wast-printer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+      "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/@yarnpkg/lockfile": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+      "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/abbrev": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-2.0.0.tgz",
+      "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/accepts/node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.18.0",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.18.0.tgz",
+      "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-import-attributes": {
+      "version": "1.9.5",
+      "resolved": "https://registry.npmmirror.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+      "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^8"
+      }
+    },
+    "node_modules/adjust-sourcemap-loader": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz",
+      "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "loader-utils": "^2.0.0",
+        "regex-parser": "^2.2.11"
+      },
+      "engines": {
+        "node": ">=8.9"
+      }
+    },
+    "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
+      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/aggregate-error": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz",
+      "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clean-stack": "^2.0.0",
+        "indent-string": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz",
+      "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/ansi-colors": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz",
+      "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "type-fest": "^0.21.3"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-html-community": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmmirror.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+      "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
+      "dev": true,
+      "engines": [
+        "node >= 0.8.0"
+      ],
+      "license": "Apache-2.0",
+      "bin": {
+        "ansi-html": "bin/ansi-html"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/anymatch/node_modules/picomatch": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz",
+      "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/autoprefixer": {
+      "version": "10.4.20",
+      "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.20.tgz",
+      "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.23.3",
+        "caniuse-lite": "^1.0.30001646",
+        "fraction.js": "^4.3.7",
+        "normalize-range": "^0.1.2",
+        "picocolors": "^1.0.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "bin": {
+        "autoprefixer": "bin/autoprefixer"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/babel-loader": {
+      "version": "9.1.3",
+      "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-9.1.3.tgz",
+      "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-cache-dir": "^4.0.0",
+        "schema-utils": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 14.15.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.12.0",
+        "webpack": ">=5"
+      }
+    },
+    "node_modules/babel-plugin-polyfill-corejs2": {
+      "version": "0.4.17",
+      "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
+      "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/compat-data": "^7.28.6",
+        "@babel/helper-define-polyfill-provider": "^0.6.8",
+        "semver": "^6.3.1"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+      }
+    },
+    "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/babel-plugin-polyfill-corejs3": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz",
+      "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-define-polyfill-provider": "^0.6.3",
+        "core-js-compat": "^3.40.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+      }
+    },
+    "node_modules/babel-plugin-polyfill-regenerator": {
+      "version": "0.6.8",
+      "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
+      "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-define-polyfill-provider": "^0.6.8"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/base64id": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/base64id/-/base64id-2.0.0.tgz",
+      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^4.5.0 || >= 5.9"
+      }
+    },
+    "node_modules/baseline-browser-mapping": {
+      "version": "2.11.11",
+      "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.11.tgz",
+      "integrity": "sha512-/yImnXwyTvgMkhgekLHok/Rx5vO6E0BmStWlSqKWMVm2a2ITuZ1Tn+9bgLS+gZRdZmWtd8nxuhHpdmCUOWsTQQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "baseline-browser-mapping": "dist/cli.cjs"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/batch": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/batch/-/batch-0.6.1.tgz",
+      "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/big.js": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz",
+      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.6",
+      "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.6.tgz",
+      "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "~1.2.0",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "on-finished": "~2.4.1",
+        "qs": "~6.15.1",
+        "raw-body": "~2.5.3",
+        "type-is": "~1.6.18",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/body-parser/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/body-parser/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/bonjour-service": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmmirror.com/bonjour-service/-/bonjour-service-1.4.4.tgz",
+      "integrity": "sha512-jCZcVv7eoc4QesRscwEZtSROBen+6LpKAmBIsQYQrsAeVHLyMXWX/t6eIV5KiRZYNUBl8eVqImEEMQ8L5+c/Kw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "multicast-dns": "^7.2.5"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.18",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz",
+      "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.28.7",
+      "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.7.tgz",
+      "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "baseline-browser-mapping": "^2.10.44",
+        "caniuse-lite": "^1.0.30001806",
+        "electron-to-chromium": "^1.5.393",
+        "node-releases": "^2.0.51",
+        "update-browserslist-db": "^1.2.3"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/bundle-name": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/bundle-name/-/bundle-name-4.1.0.tgz",
+      "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "run-applescript": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/cacache": {
+      "version": "18.0.4",
+      "resolved": "https://registry.npmmirror.com/cacache/-/cacache-18.0.4.tgz",
+      "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/fs": "^3.1.0",
+        "fs-minipass": "^3.0.0",
+        "glob": "^10.2.2",
+        "lru-cache": "^10.0.1",
+        "minipass": "^7.0.3",
+        "minipass-collect": "^2.0.1",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "p-map": "^4.0.0",
+        "ssri": "^10.0.0",
+        "tar": "^6.1.11",
+        "unique-filename": "^3.0.0"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/cacache/node_modules/brace-expansion": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz",
+      "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/cacache/node_modules/glob": {
+      "version": "10.4.5",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz",
+      "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/cacache/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/cacache/node_modules/minimatch": {
+      "version": "9.0.9",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
+      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001806",
+      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
+      "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "CC-BY-4.0"
+    },
+    "node_modules/canvg": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmmirror.com/canvg/-/canvg-3.0.11.tgz",
+      "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "@types/raf": "^3.4.0",
+        "core-js": "^3.8.3",
+        "raf": "^3.4.1",
+        "regenerator-runtime": "^0.13.7",
+        "rgbcolor": "^1.0.1",
+        "stackblur-canvas": "^2.0.0",
+        "svg-pathdata": "^6.0.3"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/canvg/node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/chokidar/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/chownr": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz",
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/chrome-trace-event": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+      "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/clean-stack": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz",
+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/cli-cursor": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-5.0.0.tgz",
+      "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "restore-cursor": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-spinners": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz",
+      "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-truncate": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-4.0.0.tgz",
+      "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "slice-ansi": "^5.0.0",
+        "string-width": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-width": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/cli-width/-/cli-width-4.1.0.tgz",
+      "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/cliui/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cliui/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cliui/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/clone-deep": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz",
+      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-plain-object": "^2.0.4",
+        "kind-of": "^6.0.2",
+        "shallow-clone": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/clone-deep/node_modules/is-plain-object": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz",
+      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/common-path-prefix": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
+      "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": ">= 1.43.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/compression": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/compression/-/compression-1.8.1.tgz",
+      "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "compressible": "~2.0.18",
+        "debug": "2.6.9",
+        "negotiator": "~0.6.4",
+        "on-headers": "~1.1.0",
+        "safe-buffer": "5.2.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/compression/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/compression/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/connect": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmmirror.com/connect/-/connect-3.7.0.tgz",
+      "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "finalhandler": "1.1.2",
+        "parseurl": "~1.3.3",
+        "utils-merge": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/connect-history-api-fallback": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
+      "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/connect/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/connect/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.9.0.tgz",
+      "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.7.tgz",
+      "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/copy-anything": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
+      "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-what": "^3.14.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/copy-webpack-plugin": {
+      "version": "12.0.2",
+      "resolved": "https://registry.npmmirror.com/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz",
+      "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-glob": "^3.3.2",
+        "glob-parent": "^6.0.1",
+        "globby": "^14.0.0",
+        "normalize-path": "^3.0.0",
+        "schema-utils": "^4.2.0",
+        "serialize-javascript": "^6.0.2"
+      },
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      }
+    },
+    "node_modules/core-js": {
+      "version": "3.50.0",
+      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.50.0.tgz",
+      "integrity": "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/core-js-compat": {
+      "version": "3.49.0",
+      "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.49.0.tgz",
+      "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.28.1"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cors": {
+      "version": "2.8.6",
+      "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.6.tgz",
+      "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/cosmiconfig": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-9.0.2.tgz",
+      "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "env-paths": "^2.2.1",
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/d-fischer"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/critters": {
+      "version": "0.0.24",
+      "resolved": "https://registry.npmmirror.com/critters/-/critters-0.0.24.tgz",
+      "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==",
+      "deprecated": "Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "css-select": "^5.1.0",
+        "dom-serializer": "^2.0.0",
+        "domhandler": "^5.0.2",
+        "htmlparser2": "^8.0.2",
+        "postcss": "^8.4.23",
+        "postcss-media-query-parser": "^0.2.3"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/cross-spawn/node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
+    "node_modules/css-loader": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-7.1.2.tgz",
+      "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "icss-utils": "^5.1.0",
+        "postcss": "^8.4.33",
+        "postcss-modules-extract-imports": "^3.1.0",
+        "postcss-modules-local-by-default": "^4.0.5",
+        "postcss-modules-scope": "^3.2.0",
+        "postcss-modules-values": "^4.0.0",
+        "postcss-value-parser": "^4.2.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "webpack": "^5.27.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/css-select": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz",
+      "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.1.0",
+        "domhandler": "^5.0.2",
+        "domutils": "^3.0.1",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz",
+      "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/custom-event": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/custom-event/-/custom-event-1.0.1.tgz",
+      "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/date-format": {
+      "version": "4.0.14",
+      "resolved": "https://registry.npmmirror.com/date-format/-/date-format-4.0.14.tgz",
+      "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/default-browser": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmmirror.com/default-browser/-/default-browser-5.5.0.tgz",
+      "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bundle-name": "^4.1.0",
+        "default-browser-id": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/default-browser-id": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/default-browser-id/-/default-browser-id-5.0.1.tgz",
+      "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/defaults": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz",
+      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/define-lazy-prop": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+      "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
+      "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/detect-node": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz",
+      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/di": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/di/-/di-0.0.1.tgz",
+      "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/dns-packet": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmmirror.com/dns-packet/-/dns-packet-5.6.1.tgz",
+      "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@leichtgewicht/ip-codec": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/dom-serialize": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/dom-serialize/-/dom-serialize-2.2.1.tgz",
+      "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "custom-event": "~1.0.0",
+        "ent": "~2.2.0",
+        "extend": "^3.0.0",
+        "void-elements": "^2.0.0"
+      }
+    },
+    "node_modules/dom-serializer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz",
+      "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.2",
+        "entities": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/domhandler": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz",
+      "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "domelementtype": "^2.3.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/dompurify": {
+      "version": "3.4.13",
+      "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.13.tgz",
+      "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==",
+      "license": "(MPL-2.0 OR Apache-2.0)",
+      "optional": true,
+      "optionalDependencies": {
+        "@types/trusted-types": "^2.0.7"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz",
+      "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dom-serializer": "^2.0.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.399",
+      "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.399.tgz",
+      "integrity": "sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/emoji-regex": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz",
+      "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/emojis-list": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz",
+      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/encoding": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz",
+      "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "iconv-lite": "^0.6.2"
+      }
+    },
+    "node_modules/encoding/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/engine.io": {
+      "version": "6.6.9",
+      "resolved": "https://registry.npmmirror.com/engine.io/-/engine.io-6.6.9.tgz",
+      "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/cors": "^2.8.12",
+        "@types/node": ">=10.0.0",
+        "@types/ws": "^8.5.12",
+        "accepts": "~1.3.4",
+        "base64id": "2.0.0",
+        "cookie": "~0.7.2",
+        "cors": "~2.8.5",
+        "debug": "~4.4.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.21.0"
+      },
+      "engines": {
+        "node": ">=10.2.0"
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/enhanced-resolve": {
+      "version": "5.24.5",
+      "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz",
+      "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.3.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/ent": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/ent/-/ent-2.2.2.tgz",
+      "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "es-errors": "^1.3.0",
+        "punycode": "^1.4.1",
+        "safe-regex-test": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/environment": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/environment/-/environment-1.1.0.tgz",
+      "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/err-code": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz",
+      "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/errno": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
+      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "prr": "~1.0.1"
+      },
+      "bin": {
+        "errno": "cli.js"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.4.tgz",
+      "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+      "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+      "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es6-promise": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmmirror.com/es6-promise/-/es6-promise-4.2.8.tgz",
+      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+      "license": "MIT"
+    },
+    "node_modules/esbuild": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.23.0.tgz",
+      "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.23.0",
+        "@esbuild/android-arm": "0.23.0",
+        "@esbuild/android-arm64": "0.23.0",
+        "@esbuild/android-x64": "0.23.0",
+        "@esbuild/darwin-arm64": "0.23.0",
+        "@esbuild/darwin-x64": "0.23.0",
+        "@esbuild/freebsd-arm64": "0.23.0",
+        "@esbuild/freebsd-x64": "0.23.0",
+        "@esbuild/linux-arm": "0.23.0",
+        "@esbuild/linux-arm64": "0.23.0",
+        "@esbuild/linux-ia32": "0.23.0",
+        "@esbuild/linux-loong64": "0.23.0",
+        "@esbuild/linux-mips64el": "0.23.0",
+        "@esbuild/linux-ppc64": "0.23.0",
+        "@esbuild/linux-riscv64": "0.23.0",
+        "@esbuild/linux-s390x": "0.23.0",
+        "@esbuild/linux-x64": "0.23.0",
+        "@esbuild/netbsd-x64": "0.23.0",
+        "@esbuild/openbsd-arm64": "0.23.0",
+        "@esbuild/openbsd-x64": "0.23.0",
+        "@esbuild/sunos-x64": "0.23.0",
+        "@esbuild/win32-arm64": "0.23.0",
+        "@esbuild/win32-ia32": "0.23.0",
+        "@esbuild/win32-x64": "0.23.0"
+      }
+    },
+    "node_modules/esbuild-wasm": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmmirror.com/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz",
+      "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esrecurse/node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/exponential-backoff": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
+      "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/express": {
+      "version": "4.22.2",
+      "resolved": "https://registry.npmmirror.com/express/-/express-4.22.2.tgz",
+      "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "~1.20.5",
+        "content-disposition": "~0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "~0.7.1",
+        "cookie-signature": "~1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "~1.3.1",
+        "fresh": "~0.5.2",
+        "http-errors": "~2.0.0",
+        "merge-descriptors": "1.0.3",
+        "methods": "~1.1.2",
+        "on-finished": "~2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "~0.1.12",
+        "proxy-addr": "~2.0.7",
+        "qs": "~6.15.1",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "~0.19.0",
+        "serve-static": "~1.16.2",
+        "setprototypeof": "1.2.0",
+        "statuses": "~2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/express/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/express/node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/express/node_modules/finalhandler": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.2.tgz",
+      "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "~2.0.2",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/express/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/express/node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/express/node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/external-editor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/external-editor/-/external-editor-3.1.0.tgz",
+      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz",
+      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-png": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmmirror.com/fast-png/-/fast-png-6.4.0.tgz",
+      "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/pako": "^2.0.3",
+        "iobuffer": "^5.3.2",
+        "pako": "^2.1.0"
+      }
+    },
+    "node_modules/fast-uri": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.5.tgz",
+      "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/fastq": {
+      "version": "1.20.1",
+      "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz",
+      "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/faye-websocket": {
+      "version": "0.11.4",
+      "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz",
+      "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "websocket-driver": ">=0.5.1"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/fflate": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.3.tgz",
+      "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
+      "license": "MIT"
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.1.2.tgz",
+      "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.3",
+        "statuses": "~1.5.0",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/finalhandler/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/finalhandler/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/finalhandler/node_modules/on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/find-cache-dir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz",
+      "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "common-path-prefix": "^3.0.0",
+        "pkg-dir": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-6.3.0.tgz",
+      "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^7.1.0",
+        "path-exists": "^5.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmmirror.com/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "bin": {
+        "flat": "cli.js"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.4.4",
+      "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.4.4.tgz",
+      "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz",
+      "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/foreground-child": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
+      "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "cross-spawn": "^7.0.6",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fraction.js": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz",
+      "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "type": "patreon",
+        "url": "https://github.com/sponsors/rawify"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fs-extra": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz",
+      "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=6 <7 || >=8"
+      }
+    },
+    "node_modules/fs-minipass": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-3.0.3.tgz",
+      "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^7.0.3"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "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,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-east-asian-width": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+      "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/glob-to-regex.js": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz",
+      "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/globby": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmmirror.com/globby/-/globby-14.1.0.tgz",
+      "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/merge-streams": "^2.1.0",
+        "fast-glob": "^3.3.3",
+        "ignore": "^7.0.3",
+        "path-type": "^6.0.0",
+        "slash": "^5.1.0",
+        "unicorn-magic": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globby/node_modules/fast-glob": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz",
+      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/globby/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/handle-thing": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/handle-thing/-/handle-thing-2.0.1.tgz",
+      "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz",
+      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hosted-git-info": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
+      "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "lru-cache": "^10.0.1"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/hosted-git-info/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/hpack.js": {
+      "version": "2.1.6",
+      "resolved": "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz",
+      "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.1",
+        "obuf": "^1.0.0",
+        "readable-stream": "^2.0.1",
+        "wbuf": "^1.1.0"
+      }
+    },
+    "node_modules/hpack.js/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/hpack.js/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/hpack.js/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "license": "MIT",
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/html2pdf.js": {
+      "version": "0.10.3",
+      "resolved": "https://registry.npmmirror.com/html2pdf.js/-/html2pdf.js-0.10.3.tgz",
+      "integrity": "sha512-RcB1sh8rs5NT3jgbN5zvvTmkmZrsUrxpZ/RI8TMbvuReNZAdJZG5TMfA2TBP6ZXxpXlWf9NB/ciLXVb6W2LbRQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es6-promise": "^4.2.5",
+        "html2canvas": "^1.0.0",
+        "jspdf": "^3.0.0"
+      }
+    },
+    "node_modules/htmlparser2": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz",
+      "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+      "dev": true,
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.0.1",
+        "entities": "^4.4.0"
+      }
+    },
+    "node_modules/http-cache-semantics": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+      "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/http-deceiver": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmmirror.com/http-deceiver/-/http-deceiver-1.2.7.tgz",
+      "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/http-errors/node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/http-parser-js": {
+      "version": "0.5.10",
+      "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz",
+      "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/http-proxy": {
+      "version": "1.18.1",
+      "resolved": "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz",
+      "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eventemitter3": "^4.0.0",
+        "follow-redirects": "^1.0.0",
+        "requires-port": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/http-proxy-agent": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/http-proxy-middleware": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz",
+      "integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-proxy": "^1.17.15",
+        "debug": "^4.3.6",
+        "http-proxy": "^1.18.1",
+        "is-glob": "^4.0.3",
+        "is-plain-object": "^5.0.0",
+        "micromatch": "^4.0.8"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
+      "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.0.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/hyperdyperid": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
+      "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.18"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/icss-utils": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/icss-utils/-/icss-utils-5.1.0.tgz",
+      "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/ignore": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.6.tgz",
+      "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/ignore-walk": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmmirror.com/ignore-walk/-/ignore-walk-6.0.5.tgz",
+      "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minimatch": "^9.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/ignore-walk/node_modules/brace-expansion": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz",
+      "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/ignore-walk/node_modules/minimatch": {
+      "version": "9.0.9",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
+      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/image-size": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz",
+      "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "image-size": "bin/image-size.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "4.3.9",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.9.tgz",
+      "integrity": "sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz",
+      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/ini": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmmirror.com/ini/-/ini-4.1.3.tgz",
+      "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/iobuffer": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmmirror.com/iobuffer/-/iobuffer-5.4.0.tgz",
+      "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
+      "license": "MIT"
+    },
+    "node_modules/ip-address": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.4.0.tgz",
+      "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/ipaddr.js": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.4.0.tgz",
+      "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.16.2",
+      "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.2.tgz",
+      "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hasown": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-docker": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz",
+      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "is-docker": "cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+      "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-inside-container": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz",
+      "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-docker": "^3.0.0"
+      },
+      "bin": {
+        "is-inside-container": "cli.js"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-lambda": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz",
+      "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/is-network-error": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmmirror.com/is-network-error/-/is-network-error-1.3.2.tgz",
+      "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
+      "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-plain-object": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz",
+      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-regex": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz",
+      "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "gopd": "^1.2.0",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-what": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
+      "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/is-wsl": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.1.tgz",
+      "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-inside-container": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/isbinaryfile": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+      "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/gjtorikian/"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/istanbul-lib-coverage": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+      "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-instrument": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+      "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@babel/core": "^7.23.9",
+        "@babel/parser": "^7.23.9",
+        "@istanbuljs/schema": "^0.1.3",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-lib-report": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+      "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^4.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-lib-source-maps": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+      "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^3.0.0",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-lib-source-maps/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/istanbul-reports": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
+      "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jackspeak": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz",
+      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/cliui": "^8.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      },
+      "optionalDependencies": {
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
+    "node_modules/jasmine-core": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/jasmine-core/-/jasmine-core-5.2.0.tgz",
+      "integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      }
+    },
+    "node_modules/jest-worker/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/jiti": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz",
+      "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jiti": "bin/jiti.js"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.1.tgz",
+      "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz",
+      "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonc-parser": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+      "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/jsonfile": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz",
+      "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+      "dev": true,
+      "license": "MIT",
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+      "dev": true,
+      "engines": [
+        "node >= 0.2.0"
+      ],
+      "license": "MIT"
+    },
+    "node_modules/jspdf": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/jspdf/-/jspdf-3.0.4.tgz",
+      "integrity": "sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.28.4",
+        "fast-png": "^6.2.0",
+        "fflate": "^0.8.1"
+      },
+      "optionalDependencies": {
+        "canvg": "^3.0.11",
+        "core-js": "^3.6.0",
+        "dompurify": "^3.2.4",
+        "html2canvas": "^1.0.0-rc.5"
+      }
+    },
+    "node_modules/jspdf/node_modules/@babel/runtime": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.7.tgz",
+      "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/karma": {
+      "version": "6.4.4",
+      "resolved": "https://registry.npmmirror.com/karma/-/karma-6.4.4.tgz",
+      "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@colors/colors": "1.5.0",
+        "body-parser": "^1.19.0",
+        "braces": "^3.0.2",
+        "chokidar": "^3.5.1",
+        "connect": "^3.7.0",
+        "di": "^0.0.1",
+        "dom-serialize": "^2.2.1",
+        "glob": "^7.1.7",
+        "graceful-fs": "^4.2.6",
+        "http-proxy": "^1.18.1",
+        "isbinaryfile": "^4.0.8",
+        "lodash": "^4.17.21",
+        "log4js": "^6.4.1",
+        "mime": "^2.5.2",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.5",
+        "qjobs": "^1.2.0",
+        "range-parser": "^1.2.1",
+        "rimraf": "^3.0.2",
+        "socket.io": "^4.7.2",
+        "source-map": "^0.6.1",
+        "tmp": "^0.2.1",
+        "ua-parser-js": "^0.7.30",
+        "yargs": "^16.1.1"
+      },
+      "bin": {
+        "karma": "bin/karma"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/karma-chrome-launcher": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz",
+      "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "which": "^1.2.1"
+      }
+    },
+    "node_modules/karma-coverage": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/karma-coverage/-/karma-coverage-2.2.1.tgz",
+      "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "istanbul-lib-coverage": "^3.2.0",
+        "istanbul-lib-instrument": "^5.1.0",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^4.0.1",
+        "istanbul-reports": "^3.0.5",
+        "minimatch": "^3.0.4"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+      "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@babel/core": "^7.12.3",
+        "@babel/parser": "^7.14.7",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/karma-coverage/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/karma-jasmine": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/karma-jasmine/-/karma-jasmine-5.1.0.tgz",
+      "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "jasmine-core": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "karma": "^6.0.0"
+      }
+    },
+    "node_modules/karma-jasmine-html-reporter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz",
+      "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "jasmine-core": "^4.0.0 || ^5.0.0",
+        "karma": "^6.0.0",
+        "karma-jasmine": "^5.0.0"
+      }
+    },
+    "node_modules/karma-jasmine/node_modules/jasmine-core": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmmirror.com/jasmine-core/-/jasmine-core-4.6.1.tgz",
+      "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/karma-source-map-support": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
+      "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "source-map-support": "^0.5.5"
+      }
+    },
+    "node_modules/karma/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/karma/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/karma/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/karma/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/karma/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/karma/node_modules/tmp": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.7.tgz",
+      "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/karma/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/karma/node_modules/yargs": {
+      "version": "16.2.2",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.2.tgz",
+      "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/karma/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/launch-editor": {
+      "version": "2.14.1",
+      "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.14.1.tgz",
+      "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picocolors": "^1.1.1",
+        "shell-quote": "^1.8.4"
+      }
+    },
+    "node_modules/less": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/less/-/less-4.2.0.tgz",
+      "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "copy-anything": "^2.0.1",
+        "parse-node-version": "^1.0.1",
+        "tslib": "^2.3.0"
+      },
+      "bin": {
+        "lessc": "bin/lessc"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "optionalDependencies": {
+        "errno": "^0.1.1",
+        "graceful-fs": "^4.1.2",
+        "image-size": "~0.5.0",
+        "make-dir": "^2.1.0",
+        "mime": "^1.4.1",
+        "needle": "^3.1.0",
+        "source-map": "~0.6.0"
+      }
+    },
+    "node_modules/less-loader": {
+      "version": "12.2.0",
+      "resolved": "https://registry.npmmirror.com/less-loader/-/less-loader-12.2.0.tgz",
+      "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "less": "^3.5.0 || ^4.0.0",
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/less/node_modules/make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/less/node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/less/node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "dev": true,
+      "license": "ISC",
+      "optional": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/less/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/license-webpack-plugin": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz",
+      "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "webpack-sources": "^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "webpack": {
+          "optional": true
+        },
+        "webpack-sources": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/listr2": {
+      "version": "8.2.4",
+      "resolved": "https://registry.npmmirror.com/listr2/-/listr2-8.2.4.tgz",
+      "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cli-truncate": "^4.0.0",
+        "colorette": "^2.0.20",
+        "eventemitter3": "^5.0.1",
+        "log-update": "^6.1.0",
+        "rfdc": "^1.4.1",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/listr2/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/listr2/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/listr2/node_modules/eventemitter3": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.4.tgz",
+      "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/listr2/node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz",
+      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/listr2/node_modules/wrap-ansi": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/lmdb": {
+      "version": "3.0.13",
+      "resolved": "https://registry.npmmirror.com/lmdb/-/lmdb-3.0.13.tgz",
+      "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "msgpackr": "^1.10.2",
+        "node-addon-api": "^6.1.0",
+        "node-gyp-build-optional-packages": "5.2.2",
+        "ordered-binary": "^1.4.1",
+        "weak-lru-cache": "^1.2.2"
+      },
+      "bin": {
+        "download-lmdb-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@lmdb/lmdb-darwin-arm64": "3.0.13",
+        "@lmdb/lmdb-darwin-x64": "3.0.13",
+        "@lmdb/lmdb-linux-arm": "3.0.13",
+        "@lmdb/lmdb-linux-arm64": "3.0.13",
+        "@lmdb/lmdb-linux-x64": "3.0.13",
+        "@lmdb/lmdb-win32-x64": "3.0.13"
+      }
+    },
+    "node_modules/loader-runner": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.2.tgz",
+      "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.11.5"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/loader-utils": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-3.3.1.tgz",
+      "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12.13.0"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-7.2.0.tgz",
+      "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^6.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.18.1",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz",
+      "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/log-update/-/log-update-6.1.0.tgz",
+      "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-escapes": "^7.0.0",
+        "cli-cursor": "^5.0.0",
+        "slice-ansi": "^7.1.0",
+        "strip-ansi": "^7.1.0",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/ansi-escapes": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+      "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "environment": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/log-update/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+      "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "get-east-asian-width": "^1.3.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/slice-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-7.1.2.tgz",
+      "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "is-fullwidth-code-point": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/log-update/node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz",
+      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/log-update/node_modules/wrap-ansi": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+      "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/log4js": {
+      "version": "6.9.1",
+      "resolved": "https://registry.npmmirror.com/log4js/-/log4js-6.9.1.tgz",
+      "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "date-format": "^4.0.14",
+        "debug": "^4.3.4",
+        "flatted": "^3.2.7",
+        "rfdc": "^1.3.0",
+        "streamroller": "^3.1.5"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/lucide-angular": {
+      "version": "0.468.0",
+      "resolved": "https://registry.npmmirror.com/lucide-angular/-/lucide-angular-0.468.0.tgz",
+      "integrity": "sha512-HU1AnetqUbhwZPfeRwvYMdDer4y/YiL5ZzrCyeQl/MY0nK4oVYEtMVqbEs2KztJMfm8UJgTRkuD7fRvpFYK/Eg==",
+      "license": "ISC",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "peerDependencies": {
+        "@angular/common": "13.x - 19.x",
+        "@angular/core": "13.x - 19.x"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.11",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.11.tgz",
+      "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz",
+      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.3"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/make-fetch-happen": {
+      "version": "13.0.1",
+      "resolved": "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz",
+      "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/agent": "^2.0.0",
+        "cacache": "^18.0.0",
+        "http-cache-semantics": "^4.1.1",
+        "is-lambda": "^1.0.1",
+        "minipass": "^7.0.2",
+        "minipass-fetch": "^3.0.0",
+        "minipass-flush": "^1.0.5",
+        "minipass-pipeline": "^1.2.4",
+        "negotiator": "^0.6.3",
+        "proc-log": "^4.2.0",
+        "promise-retry": "^2.0.1",
+        "ssri": "^10.0.0"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/memfs": {
+      "version": "4.64.0",
+      "resolved": "https://registry.npmmirror.com/memfs/-/memfs-4.64.0.tgz",
+      "integrity": "sha512-Kw72fgY7Wn+sD8KmtNWSafl1dz0UvAsE/PHs3YVfLiaZuA3HxNm9sRLqAu0ATiBGJvME1PxZXbBZPv5GycDeAw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@jsonjoy.com/fs-core": "4.64.0",
+        "@jsonjoy.com/fs-fsa": "4.64.0",
+        "@jsonjoy.com/fs-node": "4.64.0",
+        "@jsonjoy.com/fs-node-builtins": "4.64.0",
+        "@jsonjoy.com/fs-node-to-fsa": "4.64.0",
+        "@jsonjoy.com/fs-node-utils": "4.64.0",
+        "@jsonjoy.com/fs-print": "4.64.0",
+        "@jsonjoy.com/fs-snapshot": "4.64.0",
+        "@jsonjoy.com/json-pack": "^1.11.0",
+        "@jsonjoy.com/util": "^1.9.0",
+        "glob-to-regex.js": "^1.0.1",
+        "thingies": "^2.5.0",
+        "tree-dump": "^1.0.3",
+        "tslib": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/micromatch/node_modules/picomatch": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz",
+      "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/mime": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
+      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/mimic-function": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/mimic-function/-/mimic-function-5.0.1.tgz",
+      "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/mini-css-extract-plugin": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz",
+      "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "schema-utils": "^4.0.0",
+        "tapable": "^2.2.1"
+      },
+      "engines": {
+        "node": ">= 12.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.0.0"
+      }
+    },
+    "node_modules/minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz",
+      "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/minipass-collect": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-2.0.1.tgz",
+      "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^7.0.3"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/minipass-fetch": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmmirror.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz",
+      "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minipass": "^7.0.3",
+        "minipass-sized": "^1.0.3",
+        "minizlib": "^2.1.2"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      },
+      "optionalDependencies": {
+        "encoding": "^0.1.13"
+      }
+    },
+    "node_modules/minipass-flush": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.7.tgz",
+      "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/minipass-flush/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-flush/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/minipass-pipeline": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+      "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-pipeline/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-pipeline/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/minipass-sized": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz",
+      "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-sized/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minipass-sized/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/minizlib": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz",
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minipass": "^3.0.0",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/minizlib/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minizlib/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/mrmime": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.0.tgz",
+      "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/msgpackr": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/msgpackr/-/msgpackr-1.12.1.tgz",
+      "integrity": "sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==",
+      "dev": true,
+      "license": "MIT",
+      "optionalDependencies": {
+        "msgpackr-extract": "^3.0.2"
+      }
+    },
+    "node_modules/msgpackr-extract": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz",
+      "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "node-gyp-build-optional-packages": "5.2.2"
+      },
+      "bin": {
+        "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4",
+        "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4",
+        "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4",
+        "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4",
+        "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4",
+        "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4"
+      }
+    },
+    "node_modules/multicast-dns": {
+      "version": "7.2.5",
+      "resolved": "https://registry.npmmirror.com/multicast-dns/-/multicast-dns-7.2.5.tgz",
+      "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dns-packet": "^5.2.2",
+        "thunky": "^1.0.2"
+      },
+      "bin": {
+        "multicast-dns": "cli.js"
+      }
+    },
+    "node_modules/mustache": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/mustache/-/mustache-4.2.0.tgz",
+      "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+      "license": "MIT",
+      "bin": {
+        "mustache": "bin/mustache"
+      }
+    },
+    "node_modules/mute-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/mute-stream/-/mute-stream-1.0.0.tgz",
+      "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.16",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.16.tgz",
+      "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/needle": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmmirror.com/needle/-/needle-3.5.0.tgz",
+      "integrity": "sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "iconv-lite": "^0.6.3",
+        "sax": "^1.2.4"
+      },
+      "bin": {
+        "needle": "bin/needle"
+      },
+      "engines": {
+        "node": ">= 4.4.x"
+      }
+    },
+    "node_modules/needle/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz",
+      "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nice-napi": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/nice-napi/-/nice-napi-1.0.2.tgz",
+      "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "!win32"
+      ],
+      "dependencies": {
+        "node-addon-api": "^3.0.0",
+        "node-gyp-build": "^4.2.2"
+      }
+    },
+    "node_modules/nice-napi/node_modules/node-addon-api": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-3.2.1.tgz",
+      "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/node-addon-api": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-6.1.0.tgz",
+      "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/node-forge": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.4.0.tgz",
+      "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
+      "dev": true,
+      "license": "(BSD-3-Clause OR GPL-2.0)",
+      "engines": {
+        "node": ">= 6.13.0"
+      }
+    },
+    "node_modules/node-gyp": {
+      "version": "10.3.1",
+      "resolved": "https://registry.npmmirror.com/node-gyp/-/node-gyp-10.3.1.tgz",
+      "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "env-paths": "^2.2.0",
+        "exponential-backoff": "^3.1.1",
+        "glob": "^10.3.10",
+        "graceful-fs": "^4.2.6",
+        "make-fetch-happen": "^13.0.0",
+        "nopt": "^7.0.0",
+        "proc-log": "^4.1.0",
+        "semver": "^7.3.5",
+        "tar": "^6.2.1",
+        "which": "^4.0.0"
+      },
+      "bin": {
+        "node-gyp": "bin/node-gyp.js"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/node-gyp-build": {
+      "version": "4.8.4",
+      "resolved": "https://registry.npmmirror.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
+      "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "node-gyp-build": "bin.js",
+        "node-gyp-build-optional": "optional.js",
+        "node-gyp-build-test": "build-test.js"
+      }
+    },
+    "node_modules/node-gyp-build-optional-packages": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmmirror.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
+      "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "detect-libc": "^2.0.1"
+      },
+      "bin": {
+        "node-gyp-build-optional-packages": "bin.js",
+        "node-gyp-build-optional-packages-optional": "optional.js",
+        "node-gyp-build-optional-packages-test": "build-test.js"
+      }
+    },
+    "node_modules/node-gyp/node_modules/brace-expansion": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.4.tgz",
+      "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/node-gyp/node_modules/glob": {
+      "version": "10.4.5",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz",
+      "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/node-gyp/node_modules/isexe": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.5.tgz",
+      "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/node-gyp/node_modules/minimatch": {
+      "version": "9.0.9",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
+      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/node-gyp/node_modules/which": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/which/-/which-4.0.0.tgz",
+      "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^3.1.1"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.51",
+      "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.51.tgz",
+      "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/nopt": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmmirror.com/nopt/-/nopt-7.2.1.tgz",
+      "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "abbrev": "^2.0.0"
+      },
+      "bin": {
+        "nopt": "bin/nopt.js"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/normalize-package-data": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
+      "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "hosted-git-info": "^7.0.0",
+        "semver": "^7.3.5",
+        "validate-npm-package-license": "^3.0.4"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/normalize-range": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz",
+      "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npm-bundled": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/npm-bundled/-/npm-bundled-3.0.1.tgz",
+      "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "npm-normalize-package-bin": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/npm-install-checks": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmmirror.com/npm-install-checks/-/npm-install-checks-6.3.0.tgz",
+      "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "semver": "^7.1.1"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/npm-normalize-package-bin": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz",
+      "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/npm-package-arg": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmmirror.com/npm-package-arg/-/npm-package-arg-11.0.3.tgz",
+      "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "hosted-git-info": "^7.0.0",
+        "proc-log": "^4.0.0",
+        "semver": "^7.3.5",
+        "validate-npm-package-name": "^5.0.0"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/npm-packlist": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmmirror.com/npm-packlist/-/npm-packlist-8.0.2.tgz",
+      "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "ignore-walk": "^6.0.4"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/npm-pick-manifest": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmmirror.com/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz",
+      "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "npm-install-checks": "^6.0.0",
+        "npm-normalize-package-bin": "^3.0.0",
+        "npm-package-arg": "^11.0.0",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/npm-registry-fetch": {
+      "version": "17.1.0",
+      "resolved": "https://registry.npmmirror.com/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz",
+      "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/redact": "^2.0.0",
+        "jsonparse": "^1.3.1",
+        "make-fetch-happen": "^13.0.0",
+        "minipass": "^7.0.2",
+        "minipass-fetch": "^3.0.0",
+        "minizlib": "^2.1.2",
+        "npm-package-arg": "^11.0.0",
+        "proc-log": "^4.0.0"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/obuf": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz",
+      "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/on-headers": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.1.0.tgz",
+      "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-7.0.0.tgz",
+      "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-function": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/open": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/open/-/open-10.1.0.tgz",
+      "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "default-browser": "^5.2.1",
+        "define-lazy-prop": "^3.0.0",
+        "is-inside-container": "^1.0.0",
+        "is-wsl": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ora": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz",
+      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bl": "^4.1.0",
+        "chalk": "^4.1.0",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.5.0",
+        "is-interactive": "^1.0.0",
+        "is-unicode-supported": "^0.1.0",
+        "log-symbols": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "wcwidth": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ora/node_modules/cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "restore-cursor": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ora/node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ora/node_modules/restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ora/node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/ordered-binary": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmmirror.com/ordered-binary/-/ordered-binary-1.6.1.tgz",
+      "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/os-tmpdir": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-4.0.0.tgz",
+      "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^1.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-6.0.0.tgz",
+      "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-map": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz",
+      "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "aggregate-error": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-retry": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-6.2.1.tgz",
+      "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/retry": "0.12.2",
+        "is-network-error": "^1.0.0",
+        "retry": "^0.13.1"
+      },
+      "engines": {
+        "node": ">=16.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-retry/node_modules/retry": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz",
+      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0"
+    },
+    "node_modules/pacote": {
+      "version": "18.0.6",
+      "resolved": "https://registry.npmmirror.com/pacote/-/pacote-18.0.6.tgz",
+      "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@npmcli/git": "^5.0.0",
+        "@npmcli/installed-package-contents": "^2.0.1",
+        "@npmcli/package-json": "^5.1.0",
+        "@npmcli/promise-spawn": "^7.0.0",
+        "@npmcli/run-script": "^8.0.0",
+        "cacache": "^18.0.0",
+        "fs-minipass": "^3.0.0",
+        "minipass": "^7.0.2",
+        "npm-package-arg": "^11.0.0",
+        "npm-packlist": "^8.0.0",
+        "npm-pick-manifest": "^9.0.0",
+        "npm-registry-fetch": "^17.0.0",
+        "proc-log": "^4.0.0",
+        "promise-retry": "^2.0.1",
+        "sigstore": "^2.2.0",
+        "ssri": "^10.0.0",
+        "tar": "^6.1.11"
+      },
+      "bin": {
+        "pacote": "bin/index.js"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/pako": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-2.2.0.tgz",
+      "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
+      "license": "(MIT AND Zlib)"
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parse-json/node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/parse-node-version": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
+      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-html-rewriting-stream": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz",
+      "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^4.3.0",
+        "parse5": "^7.0.0",
+        "parse5-sax-parser": "^7.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-sax-parser": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz",
+      "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parse5": "^7.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5/node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-5.0.0.tgz",
+      "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/path-scurry": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/path-scurry/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/path-to-regexp": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+      "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/path-type": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/path-type/-/path-type-6.0.0.tgz",
+      "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
+      "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/piscina": {
+      "version": "4.6.1",
+      "resolved": "https://registry.npmmirror.com/piscina/-/piscina-4.6.1.tgz",
+      "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==",
+      "dev": true,
+      "license": "MIT",
+      "optionalDependencies": {
+        "nice-napi": "^1.0.2"
+      }
+    },
+    "node_modules/pkg-dir": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-7.0.0.tgz",
+      "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-up": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.4.41",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.41.tgz",
+      "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
+      "dev": true,
+      "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"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.1",
+        "source-map-js": "^1.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-loader": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmmirror.com/postcss-loader/-/postcss-loader-8.1.1.tgz",
+      "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cosmiconfig": "^9.0.0",
+        "jiti": "^1.20.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "postcss": "^7.0.0 || ^8.0.1",
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss-media-query-parser": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmmirror.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
+      "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/postcss-modules-extract-imports": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+      "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-modules-local-by-default": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
+      "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "icss-utils": "^5.0.0",
+        "postcss-selector-parser": "^7.0.0",
+        "postcss-value-parser": "^4.1.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-modules-scope": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmmirror.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
+      "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "postcss-selector-parser": "^7.0.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-modules-values": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+      "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "icss-utils": "^5.0.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >= 14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz",
+      "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-value-parser": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/proc-log": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/proc-log/-/proc-log-4.2.0.tgz",
+      "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/promise-inflight": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz",
+      "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/promise-retry": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz",
+      "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "err-code": "^2.0.2",
+        "retry": "^0.12.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-addr/node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/punycode": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-1.4.1.tgz",
+      "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/qjobs": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/qjobs/-/qjobs-1.2.0.tgz",
+      "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.9"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.15.3",
+      "resolved": "https://registry.npmmirror.com/qs/-/qs-6.15.3.tgz",
+      "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "es-define-property": "^1.0.1",
+        "side-channel": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/raf": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz",
+      "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "performance-now": "^2.1.0"
+      }
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.3.0.tgz",
+      "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.3.tgz",
+      "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.4.24",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/readdirp/node_modules/picomatch": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz",
+      "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/reflect-metadata": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+      "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/regenerate": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz",
+      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/regenerate-unicode-properties": {
+      "version": "10.2.2",
+      "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
+      "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "regenerate": "^1.4.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/regex-parser": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/regex-parser/-/regex-parser-2.3.1.tgz",
+      "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/regexpu-core": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.4.0.tgz",
+      "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "regenerate": "^1.4.2",
+        "regenerate-unicode-properties": "^10.2.2",
+        "regjsgen": "^0.8.0",
+        "regjsparser": "^0.13.0",
+        "unicode-match-property-ecmascript": "^2.0.0",
+        "unicode-match-property-value-ecmascript": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/regjsgen": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz",
+      "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/regjsparser": {
+      "version": "0.13.2",
+      "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.13.2.tgz",
+      "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "jsesc": "~3.1.0"
+      },
+      "bin": {
+        "regjsparser": "bin/parser"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/requires-port": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
+      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/resolve": {
+      "version": "1.22.8",
+      "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
+      "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-core-module": "^2.13.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/resolve-url-loader": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz",
+      "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "adjust-sourcemap-loader": "^4.0.0",
+        "convert-source-map": "^1.7.0",
+        "loader-utils": "^2.0.0",
+        "postcss": "^8.2.14",
+        "source-map": "0.6.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/resolve-url-loader/node_modules/loader-utils": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
+      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
+    "node_modules/resolve-url-loader/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/restore-cursor": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-5.1.0.tgz",
+      "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "onetime": "^7.0.0",
+        "signal-exit": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
+      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/rgbcolor": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/rgbcolor/-/rgbcolor-1.0.1.tgz",
+      "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
+      "license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
+      "optional": true,
+      "engines": {
+        "node": ">= 0.8.15"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "deprecated": "Rimraf versions prior to v4 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.22.4",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.22.4.tgz",
+      "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.5"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.22.4",
+        "@rollup/rollup-android-arm64": "4.22.4",
+        "@rollup/rollup-darwin-arm64": "4.22.4",
+        "@rollup/rollup-darwin-x64": "4.22.4",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
+        "@rollup/rollup-linux-arm-musleabihf": "4.22.4",
+        "@rollup/rollup-linux-arm64-gnu": "4.22.4",
+        "@rollup/rollup-linux-arm64-musl": "4.22.4",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
+        "@rollup/rollup-linux-riscv64-gnu": "4.22.4",
+        "@rollup/rollup-linux-s390x-gnu": "4.22.4",
+        "@rollup/rollup-linux-x64-gnu": "4.22.4",
+        "@rollup/rollup-linux-x64-musl": "4.22.4",
+        "@rollup/rollup-win32-arm64-msvc": "4.22.4",
+        "@rollup/rollup-win32-ia32-msvc": "4.22.4",
+        "@rollup/rollup-win32-x64-msvc": "4.22.4",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/run-applescript": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.1.0.tgz",
+      "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.2.tgz",
+      "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safe-regex-test": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+      "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "is-regex": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/sass": {
+      "version": "1.77.6",
+      "resolved": "https://registry.npmmirror.com/sass/-/sass-1.77.6.tgz",
+      "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/sass-loader": {
+      "version": "16.0.0",
+      "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-16.0.0.tgz",
+      "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "neo-async": "^2.6.2"
+      },
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
+        "sass": "^1.3.0",
+        "sass-embedded": "*",
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "node-sass": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/sax": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmmirror.com/sax/-/sax-1.6.1.tgz",
+      "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "optional": true,
+      "engines": {
+        "node": ">=11.0.0"
+      }
+    },
+    "node_modules/schema-utils": {
+      "version": "4.3.3",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
+      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/schema-utils/node_modules/ajv-formats": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz",
+      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/select-hose": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
+      "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/selfsigned": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmmirror.com/selfsigned/-/selfsigned-2.4.1.tgz",
+      "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node-forge": "^1.3.0",
+        "node-forge": "^1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/semver": {
+      "version": "7.6.3",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz",
+      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/send": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npmmirror.com/send/-/send-0.19.2.tgz",
+      "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "~0.5.2",
+        "http-errors": "~2.0.1",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "~2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "~2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/send/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/send/node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/send/node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/send/node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/send/node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "node_modules/serve-index": {
+      "version": "1.9.2",
+      "resolved": "https://registry.npmmirror.com/serve-index/-/serve-index-1.9.2.tgz",
+      "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "batch": "0.6.1",
+        "debug": "2.6.9",
+        "escape-html": "~1.0.3",
+        "http-errors": "~1.8.0",
+        "mime-types": "~2.1.35",
+        "parseurl": "~1.3.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/serve-index/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/serve-index/node_modules/depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/serve-index/node_modules/http-errors": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-1.8.1.tgz",
+      "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/serve-index/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/serve-static": {
+      "version": "1.16.3",
+      "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.3.tgz",
+      "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "~0.19.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/serve-static/node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/shallow-clone": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz",
+      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "kind-of": "^6.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shell-quote": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.10.0.tgz",
+      "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.1.tgz",
+      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.4",
+        "side-channel-list": "^1.0.1",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz",
+      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/sigstore": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/sigstore/-/sigstore-2.3.1.tgz",
+      "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@sigstore/bundle": "^2.3.2",
+        "@sigstore/core": "^1.0.0",
+        "@sigstore/protobuf-specs": "^0.3.2",
+        "@sigstore/sign": "^2.3.2",
+        "@sigstore/tuf": "^2.3.4",
+        "@sigstore/verify": "^1.2.1"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/slash": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/slash/-/slash-5.1.0.tgz",
+      "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/slice-ansi": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-5.0.0.tgz",
+      "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.0.0",
+        "is-fullwidth-code-point": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/slice-ansi/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socket.io": {
+      "version": "4.8.3",
+      "resolved": "https://registry.npmmirror.com/socket.io/-/socket.io-4.8.3.tgz",
+      "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.4",
+        "base64id": "~2.0.0",
+        "cors": "~2.8.5",
+        "debug": "~4.4.1",
+        "engine.io": "~6.6.0",
+        "socket.io-adapter": "~2.5.2",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.2.0"
+      }
+    },
+    "node_modules/socket.io-adapter": {
+      "version": "2.5.8",
+      "resolved": "https://registry.npmmirror.com/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz",
+      "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "~4.4.1",
+        "ws": "~8.21.0"
+      }
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.2.7",
+      "resolved": "https://registry.npmmirror.com/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
+      "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.4.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/sockjs": {
+      "version": "0.3.24",
+      "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz",
+      "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "faye-websocket": "^0.11.3",
+        "uuid": "^8.3.2",
+        "websocket-driver": "^0.7.4"
+      }
+    },
+    "node_modules/socks": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.9.tgz",
+      "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ip-address": "^10.1.1",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socks-proxy-agent": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+      "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "^4.3.4",
+        "socks": "^2.8.3"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "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==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-loader": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/source-map-loader/-/source-map-loader-5.0.0.tgz",
+      "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "^0.6.3",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.72.1"
+      }
+    },
+    "node_modules/source-map-loader/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/source-map-support/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/spdx-correct": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz",
+      "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-exceptions": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+      "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
+      "dev": true,
+      "license": "CC-BY-3.0"
+    },
+    "node_modules/spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-license-ids": {
+      "version": "3.0.23",
+      "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
+      "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
+      "dev": true,
+      "license": "CC0-1.0"
+    },
+    "node_modules/spdy": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/spdy/-/spdy-4.0.2.tgz",
+      "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.1.0",
+        "handle-thing": "^2.0.0",
+        "http-deceiver": "^1.2.7",
+        "select-hose": "^2.0.0",
+        "spdy-transport": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/spdy-transport": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/spdy-transport/-/spdy-transport-3.0.0.tgz",
+      "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.1.0",
+        "detect-node": "^2.0.4",
+        "hpack.js": "^2.1.6",
+        "obuf": "^1.1.2",
+        "readable-stream": "^3.0.6",
+        "wbuf": "^1.7.3"
+      }
+    },
+    "node_modules/ssri": {
+      "version": "10.0.6",
+      "resolved": "https://registry.npmmirror.com/ssri/-/ssri-10.0.6.tgz",
+      "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^7.0.3"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/stackblur-canvas": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
+      "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.1.14"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/streamroller": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmmirror.com/streamroller/-/streamroller-3.1.5.tgz",
+      "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "date-format": "^4.0.14",
+        "debug": "^4.3.4",
+        "fs-extra": "^8.1.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/string-width-cjs": {
+      "name": "string-width",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width-cjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/string-width/node_modules/strip-ansi": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz",
+      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.2.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi-cjs": {
+      "name": "strip-ansi",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/svg-pathdata": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
+      "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/symbol-observable": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/symbol-observable/-/symbol-observable-4.0.0.tgz",
+      "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/tapable": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.3.3.tgz",
+      "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/tar": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz",
+      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "minipass": "^5.0.0",
+        "minizlib": "^2.1.1",
+        "mkdirp": "^1.0.3",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/tar/node_modules/fs-minipass": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/tar/node_modules/minipass": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz",
+      "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/tar/node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/tar/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/terser": {
+      "version": "5.31.6",
+      "resolved": "https://registry.npmmirror.com/terser/-/terser-5.31.6.tgz",
+      "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser-webpack-plugin": {
+      "version": "5.6.1",
+      "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz",
+      "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jest-worker": "^27.4.5",
+        "schema-utils": "^4.3.0",
+        "terser": "^5.31.1"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@minify-html/node": {
+          "optional": true
+        },
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/css": {
+          "optional": true
+        },
+        "@swc/html": {
+          "optional": true
+        },
+        "clean-css": {
+          "optional": true
+        },
+        "cssnano": {
+          "optional": true
+        },
+        "csso": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "html-minifier-terser": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "postcss": {
+          "optional": true
+        },
+        "uglify-js": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
+    "node_modules/thingies": {
+      "version": "2.6.1",
+      "resolved": "https://registry.npmmirror.com/thingies/-/thingies-2.6.1.tgz",
+      "integrity": "sha512-cV/CMGTK3M4MlnJ/0At6ismOw/A0EEniDNScajjz/Br3c1sqE72YD01rGpPTKwd27wAxI5Pr+6+0w8yofzFRYw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "^2"
+      }
+    },
+    "node_modules/thunky": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz",
+      "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "os-tmpdir": "~1.0.2"
+      },
+      "engines": {
+        "node": ">=0.6.0"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/tree-dump": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/tree-dump/-/tree-dump-1.1.0.tgz",
+      "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/streamich"
+      },
+      "peerDependencies": {
+        "tslib": "2"
+      }
+    },
+    "node_modules/tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "tree-kill": "cli.js"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
+    "node_modules/tuf-js": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/tuf-js/-/tuf-js-2.2.1.tgz",
+      "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@tufjs/models": "2.0.1",
+        "debug": "^4.3.4",
+        "make-fetch-happen": "^13.0.1"
+      },
+      "engines": {
+        "node": "^16.14.0 || >=18.0.0"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typed-assert": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmmirror.com/typed-assert/-/typed-assert-1.0.9.tgz",
+      "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/typescript": {
+      "version": "5.5.4",
+      "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz",
+      "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/ua-parser-js": {
+      "version": "0.7.41",
+      "resolved": "https://registry.npmmirror.com/ua-parser-js/-/ua-parser-js-0.7.41.tgz",
+      "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/ua-parser-js"
+        },
+        {
+          "type": "paypal",
+          "url": "https://paypal.me/faisalman"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/faisalman"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "ua-parser-js": "script/cli.js"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/unicode-canonical-property-names-ecmascript": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
+      "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/unicode-match-property-ecmascript": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+      "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "unicode-canonical-property-names-ecmascript": "^2.0.0",
+        "unicode-property-aliases-ecmascript": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/unicode-match-property-value-ecmascript": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
+      "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/unicode-property-aliases-ecmascript": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
+      "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/unicorn-magic": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+      "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/unique-filename": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-3.0.0.tgz",
+      "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "unique-slug": "^4.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/unique-slug": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-4.0.0.tgz",
+      "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "imurmurhash": "^0.1.4"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+      "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/uri-js/node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "license": "MIT",
+      "dependencies": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
+    "node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "node_modules/validate-npm-package-name": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz",
+      "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/vite": {
+      "version": "5.4.21",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
+      "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/vite/node_modules/postcss": {
+      "version": "8.5.25",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.25.tgz",
+      "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==",
+      "dev": true,
+      "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"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.16",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/void-elements": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/void-elements/-/void-elements-2.0.1.tgz",
+      "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/watchpack": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.1.tgz",
+      "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/wbuf": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz",
+      "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimalistic-assert": "^1.0.0"
+      }
+    },
+    "node_modules/wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/weak-lru-cache": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
+      "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/webpack": {
+      "version": "5.94.0",
+      "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.94.0.tgz",
+      "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.5",
+        "@webassemblyjs/ast": "^1.12.1",
+        "@webassemblyjs/wasm-edit": "^1.12.1",
+        "@webassemblyjs/wasm-parser": "^1.12.1",
+        "acorn": "^8.7.1",
+        "acorn-import-attributes": "^1.9.5",
+        "browserslist": "^4.21.10",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^5.17.1",
+        "es-module-lexer": "^1.2.1",
+        "eslint-scope": "5.1.1",
+        "events": "^3.2.0",
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.2.11",
+        "json-parse-even-better-errors": "^2.3.1",
+        "loader-runner": "^4.2.0",
+        "mime-types": "^2.1.27",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^3.2.0",
+        "tapable": "^2.1.1",
+        "terser-webpack-plugin": "^5.3.10",
+        "watchpack": "^2.4.1",
+        "webpack-sources": "^3.2.3"
+      },
+      "bin": {
+        "webpack": "bin/webpack.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependenciesMeta": {
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-dev-middleware": {
+      "version": "7.4.2",
+      "resolved": "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz",
+      "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "colorette": "^2.0.10",
+        "memfs": "^4.6.0",
+        "mime-types": "^2.1.31",
+        "on-finished": "^2.4.1",
+        "range-parser": "^1.2.1",
+        "schema-utils": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-dev-server": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-5.2.2.tgz",
+      "integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/bonjour": "^3.5.13",
+        "@types/connect-history-api-fallback": "^1.5.4",
+        "@types/express": "^4.17.21",
+        "@types/express-serve-static-core": "^4.17.21",
+        "@types/serve-index": "^1.9.4",
+        "@types/serve-static": "^1.15.5",
+        "@types/sockjs": "^0.3.36",
+        "@types/ws": "^8.5.10",
+        "ansi-html-community": "^0.0.8",
+        "bonjour-service": "^1.2.1",
+        "chokidar": "^3.6.0",
+        "colorette": "^2.0.10",
+        "compression": "^1.7.4",
+        "connect-history-api-fallback": "^2.0.0",
+        "express": "^4.21.2",
+        "graceful-fs": "^4.2.6",
+        "http-proxy-middleware": "^2.0.9",
+        "ipaddr.js": "^2.1.0",
+        "launch-editor": "^2.6.1",
+        "open": "^10.0.3",
+        "p-retry": "^6.2.0",
+        "schema-utils": "^4.2.0",
+        "selfsigned": "^2.4.1",
+        "serve-index": "^1.9.1",
+        "sockjs": "^0.3.24",
+        "spdy": "^4.0.2",
+        "webpack-dev-middleware": "^7.4.2",
+        "ws": "^8.18.0"
+      },
+      "bin": {
+        "webpack-dev-server": "bin/webpack-dev-server.js"
+      },
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "webpack": {
+          "optional": true
+        },
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz",
+      "integrity": "sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-proxy": "^1.17.8",
+        "http-proxy": "^1.18.1",
+        "is-glob": "^4.0.1",
+        "is-plain-obj": "^3.0.0",
+        "micromatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "@types/express": "^4.17.13"
+      },
+      "peerDependenciesMeta": {
+        "@types/express": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-merge": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-6.0.1.tgz",
+      "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone-deep": "^4.0.1",
+        "flat": "^5.0.2",
+        "wildcard": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/webpack-sources": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.5.1.tgz",
+      "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/webpack-subresource-integrity": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz",
+      "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "typed-assert": "^1.0.8"
+      },
+      "engines": {
+        "node": ">= 12"
+      },
+      "peerDependencies": {
+        "html-webpack-plugin": ">= 5.0.0-beta.1 < 6",
+        "webpack": "^5.12.0"
+      },
+      "peerDependenciesMeta": {
+        "html-webpack-plugin": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack/node_modules/ajv": {
+      "version": "6.15.0",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz",
+      "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/webpack/node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
+    "node_modules/webpack/node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/webpack/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/webpack/node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/websocket-driver": {
+      "version": "0.7.5",
+      "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.5.tgz",
+      "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "http-parser-js": ">=0.5.1",
+        "safe-buffer": ">=5.1.0",
+        "websocket-extensions": ">=0.1.1"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/websocket-extensions": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+      "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
+      }
+    },
+    "node_modules/wildcard": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.1.tgz",
+      "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi-cjs": {
+      "name": "wrap-ansi",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/ws": {
+      "version": "8.21.1",
+      "resolved": "https://registry.npmmirror.com/ws/-/ws-8.21.1.tgz",
+      "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yargs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.2.2.tgz",
+      "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/yoctocolors-cjs": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmmirror.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+      "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zone.js": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/zone.js/-/zone.js-0.14.10.tgz",
+      "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
+      "license": "MIT"
+    }
+  }
+}

+ 45 - 0
package.json

@@ -0,0 +1,45 @@
+{
+  "name": "xiaoshu-angular",
+  "version": "0.0.0",
+  "scripts": {
+    "ng": "ng",
+    "start": "ng serve --proxy-config proxy.conf.cjs",
+    "build": "ng build",
+    "parity:generate": "node scripts/generate-source-parity.mjs",
+    "parity:check": "node scripts/generate-source-parity.mjs --check",
+    "watch": "ng build --watch --configuration development",
+    "test": "ng test",
+    "test:ci": "ng test --no-watch --no-progress --browsers=ChromeHeadless"
+  },
+  "private": true,
+  "dependencies": {
+    "@angular/animations": "^18.2.0",
+    "@angular/common": "^18.2.0",
+    "@angular/compiler": "^18.2.0",
+    "@angular/core": "^18.2.0",
+    "@angular/forms": "^18.2.0",
+    "@angular/platform-browser": "^18.2.0",
+    "@angular/platform-browser-dynamic": "^18.2.0",
+    "@angular/router": "^18.2.0",
+    "html2pdf.js": "^0.10.3",
+    "lucide-angular": "^0.468.0",
+    "mustache": "^4.2.0",
+    "rxjs": "~7.8.0",
+    "tslib": "^2.3.0",
+    "zone.js": "~0.14.10"
+  },
+  "devDependencies": {
+    "@angular-devkit/build-angular": "^18.2.21",
+    "@angular/cli": "^18.2.21",
+    "@angular/compiler-cli": "^18.2.0",
+    "@types/jasmine": "~5.1.0",
+    "@types/mustache": "^4.2.6",
+    "jasmine-core": "~5.2.0",
+    "karma": "~6.4.0",
+    "karma-chrome-launcher": "~3.2.0",
+    "karma-coverage": "~2.2.0",
+    "karma-jasmine": "~5.1.0",
+    "karma-jasmine-html-reporter": "~2.1.0",
+    "typescript": "~5.5.2"
+  }
+}

+ 25 - 0
proxy.conf.cjs

@@ -0,0 +1,25 @@
+const aiToken = process.env.FMODE_API_TOKEN || '';
+
+module.exports = {
+  '/backend': {
+    target: 'https://test.fmode.cn', secure: true, changeOrigin: true, logLevel: 'warn',
+    pathRewrite: { '^/backend': '/engxs' },
+  },
+  '/bytedesk-api': {
+    target: 'https://uniapp.bytedesk.com', secure: true, changeOrigin: true, logLevel: 'warn',
+    pathRewrite: { '^/bytedesk-api': '' },
+  },
+  '/bytedesk-upload': {
+    target: 'https://upload.bytedesk.com', secure: true, changeOrigin: true, logLevel: 'warn',
+    pathRewrite: { '^/bytedesk-upload': '' },
+  },
+  '/ncloud': {
+    target: 'https://server.xiaoshu.pro', secure: true, changeOrigin: true, logLevel: 'warn',
+    pathRewrite: { '^/ncloud': '/parse' },
+  },
+  '/fmode-api': {
+    target: 'https://api.fmode.cn', secure: true, changeOrigin: true, logLevel: 'warn',
+    pathRewrite: { '^/fmode-api': '' },
+    headers: aiToken ? { Authorization: `Bearer ${aiToken}` } : {},
+  },
+};

BIN
public/favicon.ico


BIN
public/static/bytedesk/cuw/cuw_normal.png


BIN
public/static/bytedesk/cuw/cuw_pressed.png


BIN
public/static/bytedesk/rate/down.png


BIN
public/static/bytedesk/rate/down_active.png


BIN
public/static/bytedesk/rate/rate_pic.png


BIN
public/static/bytedesk/rate/rate_star.png


BIN
public/static/bytedesk/rate/rate_star_active.png


BIN
public/static/bytedesk/rate/up.png


BIN
public/static/bytedesk/rate/up_active.png


+ 363 - 0
public/static/common/js/touch-emulator.js

@@ -0,0 +1,363 @@
+(function(window, document, exportName, undefined) {
+    "use strict";
+
+    var isMultiTouch = false;
+    var multiTouchStartPos;
+    var eventTarget;
+    var touchElements = {};
+
+    // polyfills
+    if(!document.createTouch) {
+        document.createTouch = function(view, target, identifier, pageX, pageY, screenX, screenY, clientX, clientY) {
+            // auto set
+            if(clientX == undefined || clientY == undefined) {
+                clientX = pageX - window.pageXOffset;
+                clientY = pageY - window.pageYOffset;
+            }
+
+            return new Touch(target, identifier, {
+                pageX: pageX,
+                pageY: pageY,
+                screenX: screenX,
+                screenY: screenY,
+                clientX: clientX,
+                clientY: clientY
+            });
+        };
+    }
+
+    if(!document.createTouchList) {
+        document.createTouchList = function() {
+            var touchList = new TouchList();
+            for (var i = 0; i < arguments.length; i++) {
+                touchList[i] = arguments[i];
+            }
+            touchList.length = arguments.length;
+            return touchList;
+        };
+    }
+
+    /**
+     * create an touch point
+     * @constructor
+     * @param target
+     * @param identifier
+     * @param pos
+     * @param deltaX
+     * @param deltaY
+     * @returns {Object} touchPoint
+     */
+    function Touch(target, identifier, pos, deltaX, deltaY) {
+        deltaX = deltaX || 0;
+        deltaY = deltaY || 0;
+
+        this.identifier = identifier;
+        this.target = target;
+        this.clientX = pos.clientX + deltaX;
+        this.clientY = pos.clientY + deltaY;
+        this.screenX = pos.screenX + deltaX;
+        this.screenY = pos.screenY + deltaY;
+        this.pageX = pos.pageX + deltaX;
+        this.pageY = pos.pageY + deltaY;
+    }
+
+    /**
+     * create empty touchlist with the methods
+     * @constructor
+     * @returns touchList
+     */
+    function TouchList() {
+        var touchList = [];
+
+        touchList.item = function(index) {
+            return this[index] || null;
+        };
+
+        // specified by Mozilla
+        touchList.identifiedTouch = function(id) {
+            return this[id + 1] || null;
+        };
+
+        return touchList;
+    }
+
+
+    /**
+     * Simple trick to fake touch event support
+     * this is enough for most libraries like Modernizr and Hammer
+     */
+    function fakeTouchSupport() {
+        var objs = [window, document.documentElement];
+        var props = ['ontouchstart', 'ontouchmove', 'ontouchcancel', 'ontouchend'];
+
+        for(var o=0; o<objs.length; o++) {
+            for(var p=0; p<props.length; p++) {
+                if(objs[o] && objs[o][props[p]] == undefined) {
+                    objs[o][props[p]] = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * we don't have to emulate on a touch device
+     * @returns {boolean}
+     */
+    function hasTouchSupport() {
+        return ("ontouchstart" in window) || // touch events
+               (window.Modernizr && window.Modernizr.touch) || // modernizr
+               (navigator.msMaxTouchPoints || navigator.maxTouchPoints) > 2; // pointer events
+    }
+
+    /**
+     * disable mouseevents on the page
+     * @param ev
+     */
+    function preventMouseEvents(ev) {
+		// 注释启用默认事件
+        // ev.preventDefault();
+        // ev.stopPropagation();
+    }
+
+    /**
+     * only trigger touches when the left mousebutton has been pressed
+     * @param touchType
+     * @returns {Function}
+     */
+    function onMouse(touchType) {
+        return function(ev) {
+            // prevent mouse events
+            preventMouseEvents(ev);
+
+            if (ev.which !== 1) {
+                return;
+            }
+
+            // The EventTarget on which the touch point started when it was first placed on the surface,
+            // even if the touch point has since moved outside the interactive area of that element.
+            // also, when the target doesnt exist anymore, we update it
+            if (ev.type == 'mousedown' || !eventTarget || (eventTarget && !eventTarget.dispatchEvent)) {
+                eventTarget = ev.target;
+            }
+
+            // shiftKey has been lost, so trigger a touchend
+            if (isMultiTouch && !ev.shiftKey) {
+                triggerTouch('touchend', ev);
+                isMultiTouch = false;
+            }
+
+            triggerTouch(touchType, ev);
+
+            // we're entering the multi-touch mode!
+            if (!isMultiTouch && ev.shiftKey) {
+                isMultiTouch = true;
+                multiTouchStartPos = {
+                    pageX: ev.pageX,
+                    pageY: ev.pageY,
+                    clientX: ev.clientX,
+                    clientY: ev.clientY,
+                    screenX: ev.screenX,
+                    screenY: ev.screenY
+                };
+                triggerTouch('touchstart', ev);
+            }
+
+            // reset
+            if (ev.type == 'mouseup') {
+                multiTouchStartPos = null;
+                isMultiTouch = false;
+                eventTarget = null;
+            }
+        }
+    }
+
+    /**
+     * trigger a touch event
+     * @param eventName
+     * @param mouseEv
+     */
+    function triggerTouch(eventName, mouseEv) {
+        var touchEvent = document.createEvent('Event');
+        touchEvent.initEvent(eventName, true, true);
+
+        touchEvent.altKey = mouseEv.altKey;
+        touchEvent.ctrlKey = mouseEv.ctrlKey;
+        touchEvent.metaKey = mouseEv.metaKey;
+        touchEvent.shiftKey = mouseEv.shiftKey;
+
+        touchEvent.touches = getActiveTouches(mouseEv, eventName);
+        touchEvent.targetTouches = getActiveTouches(mouseEv, eventName);
+        touchEvent.changedTouches = getChangedTouches(mouseEv, eventName);
+
+        eventTarget.dispatchEvent(touchEvent);
+    }
+
+    /**
+     * create a touchList based on the mouse event
+     * @param mouseEv
+     * @returns {TouchList}
+     */
+    function createTouchList(mouseEv) {
+        var touchList = new TouchList();
+
+        if (isMultiTouch) {
+            var f = TouchEmulator.multiTouchOffset;
+            var deltaX = multiTouchStartPos.pageX - mouseEv.pageX;
+            var deltaY = multiTouchStartPos.pageY - mouseEv.pageY;
+
+            touchList.push(new Touch(eventTarget, 1, multiTouchStartPos, (deltaX*-1) - f, (deltaY*-1) + f));
+            touchList.push(new Touch(eventTarget, 2, multiTouchStartPos, deltaX+f, deltaY-f));
+        } else {
+            touchList.push(new Touch(eventTarget, 1, mouseEv, 0, 0));
+        }
+
+        return touchList;
+    }
+
+    /**
+     * receive all active touches
+     * @param mouseEv
+     * @returns {TouchList}
+     */
+    function getActiveTouches(mouseEv, eventName) {
+        // empty list
+        if (mouseEv.type == 'mouseup') {
+            return new TouchList();
+        }
+
+        var touchList = createTouchList(mouseEv);
+        if(isMultiTouch && mouseEv.type != 'mouseup' && eventName == 'touchend') {
+            touchList.splice(1, 1);
+        }
+        return touchList;
+    }
+
+    /**
+     * receive a filtered set of touches with only the changed pointers
+     * @param mouseEv
+     * @param eventName
+     * @returns {TouchList}
+     */
+    function getChangedTouches(mouseEv, eventName) {
+        var touchList = createTouchList(mouseEv);
+
+        // we only want to return the added/removed item on multitouch
+        // which is the second pointer, so remove the first pointer from the touchList
+        //
+        // but when the mouseEv.type is mouseup, we want to send all touches because then
+        // no new input will be possible
+        if(isMultiTouch && mouseEv.type != 'mouseup' &&
+            (eventName == 'touchstart' || eventName == 'touchend')) {
+            touchList.splice(0, 1);
+        }
+
+        return touchList;
+    }
+
+    /**
+     * show the touchpoints on the screen
+     */
+    function showTouches(ev) {
+        var touch, i, el, styles;
+
+        // first all visible touches
+        for(i = 0; i < ev.touches.length; i++) {
+            touch = ev.touches[i];
+            el = touchElements[touch.identifier];
+            if(!el) {
+                el = touchElements[touch.identifier] = document.createElement("div");
+                document.body.appendChild(el);
+            }
+
+            styles = TouchEmulator.template(touch);
+            for(var prop in styles) {
+                el.style[prop] = styles[prop];
+            }
+        }
+
+        // remove all ended touches
+        if(ev.type == 'touchend' || ev.type == 'touchcancel') {
+            for(i = 0; i < ev.changedTouches.length; i++) {
+                touch = ev.changedTouches[i];
+                el = touchElements[touch.identifier];
+                if(el) {
+                    el.parentNode.removeChild(el);
+                    delete touchElements[touch.identifier];
+                }
+            }
+        }
+    }
+
+    /**
+     * TouchEmulator initializer
+     */
+    function TouchEmulator() {
+        if (hasTouchSupport()) {
+            return;
+        }
+
+        fakeTouchSupport();
+
+        window.addEventListener("mousedown", onMouse('touchstart'), true);
+        window.addEventListener("mousemove", onMouse('touchmove'), true);
+        window.addEventListener("mouseup", onMouse('touchend'), true);
+
+        window.addEventListener("mouseenter", preventMouseEvents, true);
+        window.addEventListener("mouseleave", preventMouseEvents, true);
+        window.addEventListener("mouseout", preventMouseEvents, true);
+        window.addEventListener("mouseover", preventMouseEvents, true);
+
+        // it uses itself!
+        window.addEventListener("touchstart", showTouches, true);
+        window.addEventListener("touchmove", showTouches, true);
+        window.addEventListener("touchend", showTouches, true);
+        window.addEventListener("touchcancel", showTouches, true);
+    }
+
+    // start distance when entering the multitouch mode
+    TouchEmulator.multiTouchOffset = 75;
+
+    /**
+     * css template for the touch rendering
+     * @param touch
+     * @returns object
+     */
+    TouchEmulator.template = function(touch) {
+        var size = 0;
+        var transform = 'translate('+ (touch.clientX-(size/2)) +'px, '+ (touch.clientY-(size/2)) +'px)';
+        return {
+            position: 'fixed',
+            left: 0,
+            top: 0,
+            background: '#fff',
+            border: 'solid 1px #999',
+            opacity: .6,
+            borderRadius: '100%',
+            height: size + 'px',
+            width: size + 'px',
+            padding: 0,
+            margin: 0,
+            display: 'block',
+            overflow: 'hidden',
+            pointerEvents: 'none',
+            webkitUserSelect: 'none',
+            mozUserSelect: 'none',
+            userSelect: 'none',
+            webkitTransform: transform,
+            mozTransform: transform,
+            transform: transform,
+            zIndex: 100
+        }
+    };
+
+    // export
+    if (typeof define == "function" && define.amd) {
+        define(function() {
+            return TouchEmulator;
+        });
+    } else if (typeof module != "undefined" && module.exports) {
+        module.exports = TouchEmulator;
+    } else {
+        window[exportName] = TouchEmulator;
+    }
+})(window, document, "TouchEmulator");

BIN
public/static/game/plane.png


BIN
public/static/game/xj.png


BIN
public/static/images/ai-reading-journey.png


BIN
public/static/images/demo1.jpg


BIN
public/static/images/demo2.jpg


BIN
public/static/images/foot_icon1.png


BIN
public/static/images/foot_icon1_active.png


BIN
public/static/images/foot_icon2.png


BIN
public/static/images/foot_icon2_active.png


BIN
public/static/images/foot_icon3.png


BIN
public/static/images/foot_icon3_active.png


BIN
public/static/images/foot_icon4.png


BIN
public/static/images/foot_icon4_active.png


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/static/images/homeicon1.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/static/images/homeicon2.svg


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/static/images/homeicon3.svg


BIN
public/static/images/i4.png


+ 1 - 0
public/static/images/index_list1.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711964070593" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13882" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M785.171864 102.424232H136.60875q-136.517822 0-136.563803 136.563803V887.436197q0 136.517822 136.563803 136.563803h648.563114q136.517822 0 136.563803-136.563803V238.988035q0-136.517822-136.563803-136.563803z m-207.305693 697.969783l-37.336636-136.448851h-172.429045l-37.336636 136.448851H222.984206l170.129991-531.541269h125.045543l170.911669 531.541269z" fill="#FFC06A" p-id="13883"></path><path d="M454.981738 349.434586h-2.873817a3810.383013 3810.383013 0 0 1-45.245381 173.785487l-15.840482 58.212045h126.447966l-15.840481-58.212045c-16.415245-55.315238-30.807323-116.309138-46.647805-173.785487z" fill="#FFC06A" p-id="13884"></path><path d="M984.040029 40.004918A136.609785 136.609785 0 0 0 887.479764 0.001379H239.031603a136.586794 136.586794 0 0 0-136.563804 123.919007v12.644797l28.945089 28.99107h72.397208L155.690898 117.437054a85.064995 85.064995 0 0 1 83.294724-66.212753h648.448161a85.317891 85.317891 0 0 1 85.432844 85.340882v648.448161a84.674156 84.674156 0 0 1-66.212754 83.294724l-63.913699-63.844728v72.397208l44.73959 44.73959h11.93209a136.356889 136.356889 0 0 0 124.539751-136.563804V136.565183a136.081002 136.081002 0 0 0-39.888586-96.560265z" fill="#FFC06A" p-id="13885"></path></svg>

+ 1 - 0
public/static/images/index_list2.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711964160520" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15387" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M467.6 466.1c-14.8 0-28.9-8.8-34.8-23.4-7.8-19.2 1.4-41.1 20.6-49L771.5 264 448.8 77.6c-18-10.4-24.1-33.3-13.7-51.3 10.4-18 33.3-24.1 51.3-13.7l389.2 224.8c12.4 7.2 19.7 20.8 18.7 35.1s-10 26.8-23.3 32.2L481.8 463.3c-4.7 1.9-9.5 2.8-14.2 2.8z" fill="#ED4CA5" p-id="15388"></path><path d="M467.6 1013c-166 0-344.7-44.9-344.7-143.4s178.7-143.4 344.7-143.4 344.7 44.9 344.7 143.4S633.6 1013 467.6 1013z m0-211.7c-170.3 0-267.4 48.5-269.6 68.3 2.2 19.8 99.3 68.2 269.6 68.2 170.2 0 267.3-48.4 269.5-68.3-2.2-19.7-99.3-68.2-269.5-68.2z m269.5 68.3z" fill="#FFD500" p-id="15389"></path><path d="M467.6 887.9c-20.7 0-37.6-16.8-37.6-37.6V52.7c0-20.7 16.8-37.6 37.6-37.6 20.7 0 37.6 16.8 37.6 37.6v797.7c0 20.7-16.9 37.5-37.6 37.5z" fill="#ED4CA5" p-id="15390"></path></svg>

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
public/static/images/index_list3.svg


+ 1 - 0
public/static/images/index_list4.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711964396455" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18764" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M0 0h1024v1024H0V0z" fill="#202425" opacity=".01" p-id="18765"></path><path d="M989.866667 512c0 263.918933-213.947733 477.866667-477.866667 477.866667S34.133333 775.918933 34.133333 512 248.081067 34.133333 512 34.133333s477.866667 213.947733 477.866667 477.866667z" fill="#FF7744" p-id="18766"></path><path d="M853.333333 512c0 188.5184-152.814933 341.333333-341.333333 341.333333S170.666667 700.5184 170.666667 512 323.4816 170.666667 512 170.666667s341.333333 152.814933 341.333333 341.333333z" fill="#FFAA44" p-id="18767"></path><path d="M428.6464 410.794667a68.266667 68.266667 0 0 1 37.376-21.6064l350.549333-71.714134a17.066667 17.066667 0 0 1 16.1792 28.023467l-237.397333 267.707733a68.266667 68.266667 0 0 1-37.376 21.6064l-350.549333 71.714134a17.066667 17.066667 0 0 1-16.1792-28.023467l237.397333-267.707733z" fill="#FFFFFF" p-id="18768"></path><path d="M580.266667 512a68.266667 68.266667 0 1 1-136.533334 0 68.266667 68.266667 0 0 1 136.533334 0z" fill="#FF7744" p-id="18769"></path></svg>

BIN
public/static/images/logo.png


Разница между файлами не показана из-за своего большого размера
+ 29 - 0
public/static/images/logo.svg


BIN
public/static/images/perfect.png


Разница между файлами не показана из-за своего большого размера
+ 8 - 0
public/static/zico.scss


+ 99 - 0
scripts/generate-source-parity.mjs

@@ -0,0 +1,99 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import { createRequire } from 'node:module';
+import { fileURLToPath } from 'node:url';
+
+const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
+const sourceRoot = path.resolve(process.env.XIAOSHU_UNIAPP_ROOT || path.join(projectRoot, '..', 'xiaoshu-uniapp'));
+const sourceRequire = createRequire(path.join(sourceRoot, 'package.json'));
+const JSON5 = sourceRequire('json5');
+const { parseComponent } = sourceRequire('vue-template-compiler');
+const parser = sourceRequire('@babel/parser');
+const pagesJson = JSON5.parse(fs.readFileSync(path.join(sourceRoot, 'pages.json'), 'utf8'));
+const pages = [
+  ...(pagesJson.pages || []).map((page) => page.path),
+  ...(pagesJson.subPackages || pagesJson.subpackages || []).flatMap((group) =>
+    (group.pages || []).map((page) => `${group.root}/${page.path}`),
+  ),
+];
+
+const rows = pages.map((pagePath) => inspectPage(pagePath));
+const actions = [...new Set(rows.flatMap((page) => page.apiCalls.map((call) => call.action)).filter((action) => action !== '<dynamic>'))].sort();
+const externalCalls = [...new Set(rows.flatMap((page) => page.externalCalls.map((call) => `${call.service}.${call.operation}`)))].sort();
+const outputPath = path.join(projectRoot, 'src/app/core/source-parity.generated.ts');
+const output = `// Generated by npm run parity:generate. Do not edit manually.\n` +
+  `export const SOURCE_PAGE_PARITY = ${JSON.stringify(rows, null, 2)} as const;\n\n` +
+  `export const SOURCE_API_ACTIONS = ${JSON.stringify(actions, null, 2)} as const;\n\n` +
+  `export const SOURCE_EXTERNAL_CALLS = ${JSON.stringify(externalCalls, null, 2)} as const;\n`;
+
+if (process.argv.includes('--check')) {
+  const current = fs.existsSync(outputPath) ? fs.readFileSync(outputPath, 'utf8') : '';
+  if (current !== output) {
+    console.error('Source parity manifest is stale. Run npm run parity:generate.');
+    process.exitCode = 1;
+  }
+} else {
+  fs.writeFileSync(outputPath, output);
+  console.log(`Generated ${rows.length} pages and ${actions.length} API actions.`);
+}
+
+function inspectPage(pagePath) {
+  const file = path.join(sourceRoot, `${pagePath}.vue`);
+  const source = fs.readFileSync(file, 'utf8');
+  const descriptor = parseComponent(source, { pad: 'line' });
+  const script = descriptor.script?.content || '';
+  const ast = parser.parse(script, { sourceType: 'module', plugins: ['optionalChaining', 'objectRestSpread'] });
+  const apiCalls = [];
+  const externalCalls = [];
+  const methods = [];
+  const navigations = [];
+  walk(ast, (node) => {
+    if (node.type === 'ObjectMethod' && node.key?.type === 'Identifier') methods.push(node.key.name);
+    if (node.type !== 'CallExpression') return;
+    const callee = memberName(node.callee);
+    const first = node.arguments?.[0];
+    const literal = first?.type === 'StringLiteral' ? first.value : '<dynamic>';
+    if (/(?:uni\.\$u\.http|this\.\$u\.http)\.(get|post)$/.test(callee)) {
+      apiCalls.push({ method: callee.endsWith('.post') ? 'post' : 'get', action: literal, line: node.loc?.start.line || 0 });
+    }
+    if (/^httpApi\.[A-Za-z0-9_]+$/.test(callee)) {
+      externalCalls.push({ service: 'bytedesk', operation: callee.slice('httpApi.'.length), line: node.loc?.start.line || 0 });
+    }
+    if (/(?:navigateTo|redirectTo|switchTab|reLaunch|\.route)$/.test(callee)) {
+      navigations.push({ call: callee, target: literal, line: node.loc?.start.line || 0 });
+    }
+  });
+  return {
+    path: pagePath,
+    sourceFile: `${pagePath}.vue`,
+    apiCalls: unique(apiCalls, (item) => `${item.method}:${item.action}:${item.line}`),
+    externalCalls: unique(externalCalls, (item) => `${item.service}:${item.operation}:${item.line}`),
+    methods: [...new Set(methods)],
+    navigations,
+  };
+}
+
+function memberName(node) {
+  if (!node) return '';
+  if (node.type === 'Identifier') return node.name;
+  if (node.type === 'ThisExpression') return 'this';
+  if (node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression') {
+    const property = node.computed ? node.property?.value ?? memberName(node.property) : memberName(node.property);
+    return `${memberName(node.object)}.${property}`;
+  }
+  return '';
+}
+
+function walk(node, visit) {
+  if (!node || typeof node !== 'object') return;
+  visit(node);
+  for (const [key, value] of Object.entries(node)) {
+    if (['loc', 'start', 'end', 'extra'].includes(key)) continue;
+    if (Array.isArray(value)) value.forEach((entry) => walk(entry, visit));
+    else if (value?.type) walk(value, visit);
+  }
+}
+
+function unique(items, key) {
+  return [...new Map(items.map((item) => [key(item), item])).values()];
+}

+ 1 - 0
src/app/app.component.html

@@ -0,0 +1 @@
+<router-outlet />

+ 4 - 0
src/app/app.component.scss

@@ -0,0 +1,4 @@
+:host {
+  display: block;
+  min-height: 100%;
+}

+ 30 - 0
src/app/app.component.spec.ts

@@ -0,0 +1,30 @@
+import { TestBed } from '@angular/core/testing';
+import { provideRouter } from '@angular/router';
+import { AppComponent } from './app.component';
+import { APP_NAME } from './core/app.constants';
+
+describe('AppComponent', () => {
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [AppComponent],
+      providers: [provideRouter([])],
+    }).compileComponents();
+  });
+
+  it('should create the app', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.componentInstance;
+    expect(app).toBeTruthy();
+  });
+
+  it('should render the router outlet', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    fixture.detectChanges();
+    const compiled = fixture.nativeElement as HTMLElement;
+    expect(compiled.querySelector('router-outlet')).not.toBeNull();
+  });
+
+  it('uses the final product name', () => {
+    expect(APP_NAME).toBe('小树陪练');
+  });
+});

+ 12 - 0
src/app/app.component.ts

@@ -0,0 +1,12 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { RouterOutlet } from '@angular/router';
+
+@Component({
+  selector: 'app-root',
+  standalone: true,
+  imports: [RouterOutlet],
+  templateUrl: './app.component.html',
+  styleUrl: './app.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppComponent {}

+ 13 - 0
src/app/app.config.ts

@@ -0,0 +1,13 @@
+import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
+import { provideHttpClient } from '@angular/common/http';
+import { provideRouter } from '@angular/router';
+
+import { routes } from './app.routes';
+
+export const appConfig: ApplicationConfig = {
+  providers: [
+    provideZoneChangeDetection({ eventCoalescing: true }),
+    provideHttpClient(),
+    provideRouter(routes),
+  ],
+};

+ 75 - 0
src/app/app.routes.ts

@@ -0,0 +1,75 @@
+import { Route, Routes } from '@angular/router';
+import { authGuard } from './core/auth.guard';
+import { PageKind } from './core/models';
+import { PAGE_CATALOG } from './core/page-catalog';
+import { AppShellComponent } from './layout/app-shell.component';
+
+type ComponentLoader = Exclude<Route['loadComponent'], undefined>;
+
+const components: Record<PageKind, ComponentLoader> = {
+  home: () => import('./features/home/home-page.component').then((module) => module.HomePageComponent),
+  account: () => import('./features/home/account-page.component').then((module) => module.AccountPageComponent),
+  article: () => import('./features/article/article-page.component').then((module) => module.ArticlePageComponent),
+  login: () => import('./features/auth/login-page.component').then((module) => module.LoginPageComponent),
+  resource: () => import('./features/resource/resource-page.component').then((module) => module.ResourcePageComponent),
+  form: () => import('./features/form/form-page.component').then((module) => module.FormPageComponent),
+  learning: () => import('./features/learning/learning-page.component').then((module) => module.LearningPageComponent),
+  game: () => import('./features/game/game-page.component').then((module) => module.GamePageComponent),
+  product: () => import('./features/product/product-page.component').then((module) => module.ProductPageComponent),
+  productList: () => import('./features/product-list/product-list-page.component').then((module) => module.ProductListPageComponent),
+  checkout: () => import('./features/checkout/checkout-page.component').then((module) => module.CheckoutPageComponent),
+  order: () => import('./features/order/order-page.component').then((module) => module.OrderPageComponent),
+  finance: () => import('./features/finance/finance-page.component').then((module) => module.FinancePageComponent),
+  appointment: () => import('./features/appointment/appointment-page.component').then((module) => module.AppointmentPageComponent),
+  courseCenter: () => import('./features/course-center/course-center-page.component').then((module) => module.CourseCenterPageComponent),
+  studyRecords: () => import('./features/study-records/study-records-page.component').then((module) => module.StudyRecordsPageComponent),
+  team: () => import('./features/team/team-page.component').then((module) => module.TeamPageComponent),
+  merchant: () => import('./features/merchant/merchant-page.component').then((module) => module.MerchantPageComponent),
+  webview: () => import('./features/webview/webview-page.component').then((module) => module.WebviewPageComponent),
+  support: () => import('./features/support/support-page.component').then((module) => module.SupportPageComponent),
+  supportFeedback: () => import('./features/support/support-feedback-page.component').then((module) => module.SupportFeedbackPageComponent),
+};
+
+const readingSetup: ComponentLoader = () => import('./features/english-reading/reading-setup.component').then((module) => module.ReadingSetupComponent);
+
+const migratedRoutes: Routes = PAGE_CATALOG.map((meta): Route => ({
+  path: meta.path,
+  loadComponent: meta.path === 'pages/iframe/index' ? readingSetup : components[meta.kind],
+  canActivate: meta.auth ? [authGuard] : undefined,
+  data: { meta },
+}));
+
+export const routes: Routes = [
+  {
+    path: '',
+    component: AppShellComponent,
+    children: [
+      { path: '', pathMatch: 'full', redirectTo: 'pages/home/index' },
+      ...migratedRoutes,
+      {
+        path: 'english',
+        loadComponent: () => import('./features/english-reading/reading-home.component').then((module) => module.ReadingHomeComponent),
+        data: { meta: { path: 'english', title: '英语AI助手', domain: 'learn', kind: 'learning', hideHeader: true, hideTabbar: true } },
+      },
+      {
+        path: 'words/go-reading',
+        loadComponent: readingSetup,
+        canActivate: [authGuard],
+        data: { meta: { path: 'words/go-reading', title: 'AI 阅读设置', domain: 'learn', kind: 'learning', hideHeader: true, hideTabbar: true } },
+      },
+      {
+        path: 'words/reading',
+        loadComponent: () => import('./features/english-reading/reading-exercise.component').then((module) => module.ReadingExerciseComponent),
+        canActivate: [authGuard],
+        data: { meta: { path: 'words/reading', title: '阅读练习', domain: 'learn', kind: 'learning', hideHeader: true, hideTabbar: true } },
+      },
+      {
+        path: 'words/reading/pdf/:sid',
+        loadComponent: () => import('./features/english-reading/reading-pdf.component').then((module) => module.ReadingPdfComponent),
+        canActivate: [authGuard],
+        data: { meta: { path: 'words/reading/pdf/:sid', title: '阅读打印', domain: 'learn', kind: 'learning', hideHeader: true, hideTabbar: true } },
+      },
+      { path: '**', redirectTo: 'pages/home/index' },
+    ],
+  },
+];

+ 70 - 0
src/app/core/all-routes.integration.spec.ts

@@ -0,0 +1,70 @@
+import { signal } from '@angular/core';
+import { provideHttpClient } from '@angular/common/http';
+import { TestBed } from '@angular/core/testing';
+import { provideRouter, Router } from '@angular/router';
+import { RouterTestingHarness } from '@angular/router/testing';
+import { Observable, of } from 'rxjs';
+import { routes } from '../app.routes';
+import { ApiService } from './api.service';
+import { BytedeskChatSession, BytedeskMessage, BytedeskService } from './bytedesk.service';
+import { ApiEnvelope, AppUser } from './models';
+import { PAGE_CATALOG } from './page-catalog';
+import { SessionService } from './session.service';
+
+class FakeApiService {
+  get<T>(): Observable<ApiEnvelope<T>> { return of({ retcode: 0, result: [] as T }); }
+  post<T>(): Observable<ApiEnvelope<T>> { return of({ retcode: 0, result: [] as T }); }
+  upload(): Observable<ApiEnvelope<string>> { return of({ retcode: 0, result: '/upload/test.png' }); }
+  assetUrl(path: unknown): string { return String(path || '/static/images/logo.png'); }
+}
+
+class FakeBytedeskService {
+  private readonly session: BytedeskChatSession = {
+    visitor: { uid: 'route-visitor', username: 'route-visitor', nickname: '访客', avatar: '', accessToken: 'test' },
+    thread: { tid: 'route-thread', topic: 'route/topic', type: 'workGroup' },
+    initialMessage: { mid: 'welcome', type: 'text', content: '欢迎', timestamp: '2026-01-01 00:00:00' },
+    statusCode: 200,
+    isRobot: false,
+  };
+  connect(): Observable<BytedeskChatSession> { return of(this.session); }
+  history(): Observable<BytedeskMessage[]> { return of([]); }
+  sendText(): Observable<BytedeskMessage[]> { return of([]); }
+  sendAttachment(): Observable<BytedeskMessage> { return of({}); }
+}
+
+describe('all migrated routes', () => {
+  const user = signal<AppUser | null>({ userId: 100, userName: 'route-test', honeyName: '路由测试', groupId: 1 });
+  const fakeSession = {
+    user,
+    token: signal('test-token'),
+    student: signal<AppUser | null>(null),
+    isLoggedIn: signal(true),
+    groupId: signal(1),
+    setLogin: (value: AppUser) => user.set(value),
+    setStudent: () => undefined,
+    patchUser: (value: Partial<AppUser>) => user.update((current) => current ? { ...current, ...value } : current),
+    logout: () => undefined,
+  };
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [
+        provideRouter(routes),
+        provideHttpClient(),
+        { provide: ApiService, useClass: FakeApiService },
+        { provide: BytedeskService, useClass: FakeBytedeskService },
+        { provide: SessionService, useValue: fakeSession },
+      ],
+    });
+  });
+
+  it('instantiates every legacy route with an authenticated session', async () => {
+    const harness = await RouterTestingHarness.create();
+    const router = TestBed.inject(Router);
+    for (const page of PAGE_CATALOG) {
+      await harness.navigateByUrl(`/${page.path}`);
+      expect(router.url).withContext(page.path).toBe(`/${page.path}`);
+      expect(document.title).withContext(page.path).toContain(page.title);
+    }
+  });
+});

+ 92 - 0
src/app/core/anti-forget.spec.ts

@@ -0,0 +1,92 @@
+import { buildAntiForgetCalendar, buildAntiForgetView, findAntiForgetList, mergeAntiForgetItems, nearestAntiForgetDate, summarizeAntiForgetHistory } from './anti-forget';
+
+describe('anti-forget view model', () => {
+  it('maps source tasks into a stable 21-day progress view', () => {
+    const view = buildAntiForgetView([
+      { kywsj: '2026-08-10 08:00:00', fxzt: 1 },
+      { kywsj: '2026-08-10 09:00:00', fxzt: 0 },
+      { kywsj: '2026-08-11 09:00:00', fxzt: 0 },
+    ], new Date(2026, 7, 10, 12));
+
+    expect(view.days.length).toBe(21);
+    expect(view.summary).toEqual({
+      total: 3,
+      completed: 1,
+      progress: 33,
+      currentDay: 1,
+      todayTotal: 2,
+      todayCompleted: 1,
+      pending: 1,
+    });
+    expect(view.days[0].isToday).toBeTrue();
+    expect(view.days[0].percent).toBe(50);
+  });
+
+  it('supports the nested list envelopes returned by the legacy API', () => {
+    const source = [{ kywsj: '2026-08-10', fxzt: 0 }];
+    expect(findAntiForgetList({ result: { list: source } })).toEqual(source);
+  });
+
+  it('returns a usable empty plan without inventing progress', () => {
+    const view = buildAntiForgetView([], new Date(2026, 7, 10, 12));
+    expect(view.days.length).toBe(21);
+    expect(view.summary.total).toBe(0);
+    expect(view.summary.progress).toBe(0);
+    expect(view.summary.currentDay).toBe(1);
+  });
+
+  it('maps sparse review dates into a real monthly calendar', () => {
+    const items = [
+      { kywsj: '2026-08-01 08:00:00', fxzt: 1 },
+      { kywsj: '2026-08-04 09:00:00', fxzt: 0 },
+      { kywsj: '2026-08-18 09:00:00', fxzt: 0 },
+    ];
+    const calendar = buildAntiForgetCalendar(items, new Date(2026, 7, 1), '2026-08-04', new Date(2026, 7, 10, 12));
+
+    expect(calendar.days.length).toBe(42);
+    expect(calendar.scheduledDays).toBe(3);
+    expect(calendar.days.find((day) => day.date === '2026-08-01')?.state).toBe('completed');
+    expect(calendar.days.find((day) => day.date === '2026-08-04')?.state).toBe('due');
+    expect(calendar.days.find((day) => day.date === '2026-08-18')?.state).toBe('scheduled');
+    expect(calendar.days.find((day) => day.date === '2026-08-04')?.isSelected).toBeTrue();
+  });
+
+  it('selects the closest pending review date instead of inventing consecutive days', () => {
+    const date = nearestAntiForgetDate([
+      { kywsj: '2026-07-01 08:00:00', fxzt: 1 },
+      { kywsj: '2026-08-08 08:00:00', fxzt: 0 },
+      { kywsj: '2026-08-18 08:00:00', fxzt: 0 },
+    ], new Date(2026, 7, 10, 12));
+    expect(date).toBe('2026-08-08');
+  });
+
+  it('summarizes every sparse historical review instead of only the first 21 calendar days', () => {
+    const summary = summarizeAntiForgetHistory([
+      { GeneralID: 1, kywsj: '2026-05-15 08:00:00', fxzt: 1 },
+      { GeneralID: 2, kywsj: '2026-08-10 08:00:00', fxzt: 0 },
+      { GeneralID: 3, kywsj: '2026-08-18 08:00:00', fxzt: 0 },
+    ], new Date(2026, 7, 10, 12));
+
+    expect(summary).toEqual({
+      total: 3,
+      completed: 1,
+      progress: 33,
+      todayTotal: 1,
+      todayCompleted: 0,
+      duePending: 1,
+      scheduled: 1,
+      scheduledDays: 3,
+    });
+  });
+
+  it('merges duplicate summary and history records with history taking precedence', () => {
+    const merged = mergeAntiForgetItems(
+      [{ GeneralID: 10, kywsj: '2020-08-01', fxzt: 0 }],
+      [{ GeneralID: 10, kywsj: '2026-08-10', fxzt: 1 }],
+    );
+
+    expect(merged.length).toBe(1);
+    expect(merged[0]['kywsj']).toBe('2026-08-10');
+    expect(merged[0]['fxzt']).toBe(1);
+  });
+});

+ 252 - 0
src/app/core/anti-forget.ts

@@ -0,0 +1,252 @@
+export type AntiForgetItem = Record<string, unknown>;
+
+export interface AntiForgetDay {
+  day: number;
+  date: string;
+  total: number;
+  completed: number;
+  percent: number;
+  isToday: boolean;
+  isPast: boolean;
+}
+
+export interface AntiForgetSummary {
+  total: number;
+  completed: number;
+  progress: number;
+  currentDay: number;
+  todayTotal: number;
+  todayCompleted: number;
+  pending: number;
+}
+
+export interface AntiForgetView {
+  days: AntiForgetDay[];
+  summary: AntiForgetSummary;
+}
+
+export interface AntiForgetHistorySummary {
+  total: number;
+  completed: number;
+  progress: number;
+  todayTotal: number;
+  todayCompleted: number;
+  duePending: number;
+  scheduled: number;
+  scheduledDays: number;
+}
+
+export type AntiForgetCalendarState = 'none' | 'scheduled' | 'due' | 'completed';
+
+export interface AntiForgetCalendarDay {
+  date: string;
+  day: number;
+  inCurrentMonth: boolean;
+  isToday: boolean;
+  isSelected: boolean;
+  total: number;
+  completed: number;
+  pending: number;
+  state: AntiForgetCalendarState;
+}
+
+export interface AntiForgetCalendarView {
+  days: AntiForgetCalendarDay[];
+  monthLabel: string;
+  scheduledDays: number;
+}
+
+export function buildAntiForgetView(items: AntiForgetItem[], now = new Date()): AntiForgetView {
+  const today = localDate(now);
+  const grouped = new Map<string, { total: number; completed: number }>();
+
+  for (const item of items) {
+    const date = antiForgetDate(item);
+    if (!date) continue;
+    const value = grouped.get(date) ?? { total: 0, completed: 0 };
+    value.total += 1;
+    if (antiForgetCompleted(item)) value.completed += 1;
+    grouped.set(date, value);
+  }
+
+  const firstDate = [...grouped.keys()].sort()[0] ?? today;
+  const days = Array.from({ length: 21 }, (_, index): AntiForgetDay => {
+    const date = shiftDate(firstDate, index);
+    const value = grouped.get(date) ?? { total: 0, completed: 0 };
+    return {
+      day: index + 1,
+      date,
+      total: value.total,
+      completed: value.completed,
+      percent: value.total ? Math.round((value.completed / value.total) * 100) : 0,
+      isToday: date === today,
+      isPast: date < today,
+    };
+  });
+
+  const total = days.reduce((sum, day) => sum + day.total, 0);
+  const completed = days.reduce((sum, day) => sum + day.completed, 0);
+  const todayData = days.find((day) => day.isToday);
+  const elapsed = days.filter((day) => day.date <= today).length;
+  const pending = days
+    .filter((day) => day.date <= today)
+    .reduce((sum, day) => sum + Math.max(0, day.total - day.completed), 0);
+
+  return {
+    days,
+    summary: {
+      total,
+      completed,
+      progress: total ? Math.round((completed / total) * 100) : 0,
+      currentDay: Math.max(1, Math.min(21, elapsed || 1)),
+      todayTotal: todayData?.total ?? 0,
+      todayCompleted: todayData?.completed ?? 0,
+      pending,
+    },
+  };
+}
+
+export function findAntiForgetList(value: unknown): AntiForgetItem[] {
+  if (Array.isArray(value)) return value as AntiForgetItem[];
+  if (!value || typeof value !== 'object') return [];
+  for (const nested of Object.values(value)) {
+    if (Array.isArray(nested)) return nested as AntiForgetItem[];
+    if (nested && typeof nested === 'object') {
+      for (const child of Object.values(nested)) {
+        if (Array.isArray(child)) return child as AntiForgetItem[];
+      }
+    }
+  }
+  return [];
+}
+
+export function mergeAntiForgetItems(...lists: AntiForgetItem[][]): AntiForgetItem[] {
+  const items = new Map<string, AntiForgetItem>();
+  let fallbackIndex = 0;
+
+  for (const list of lists) {
+    for (const item of list) {
+      const id = item['GeneralID'] ?? item['generalId'] ?? item['id'];
+      const key = id == null || id === ''
+        ? `${antiForgetDate(item)}|${String(item['Title'] ?? '')}|${String(item['kc_title'] ?? '')}|${fallbackIndex++}`
+        : `id:${String(id)}`;
+      const previous = items.get(key);
+      items.set(key, previous ? { ...previous, ...item } : item);
+    }
+  }
+
+  return [...items.values()].sort((left, right) => antiForgetDate(left).localeCompare(antiForgetDate(right)));
+}
+
+export function summarizeAntiForgetHistory(items: AntiForgetItem[], now = new Date()): AntiForgetHistorySummary {
+  const today = localDate(now);
+  const tasks = items.filter((item) => antiForgetDate(item));
+  const completed = tasks.filter(antiForgetCompleted).length;
+  const todayTasks = tasks.filter((item) => antiForgetDate(item) === today);
+  const pendingTasks = tasks.filter((item) => !antiForgetCompleted(item));
+
+  return {
+    total: tasks.length,
+    completed,
+    progress: tasks.length ? Math.round((completed / tasks.length) * 100) : 0,
+    todayTotal: todayTasks.length,
+    todayCompleted: todayTasks.filter(antiForgetCompleted).length,
+    duePending: pendingTasks.filter((item) => antiForgetDate(item) <= today).length,
+    scheduled: pendingTasks.filter((item) => antiForgetDate(item) > today).length,
+    scheduledDays: new Set(tasks.map(antiForgetDate)).size,
+  };
+}
+
+export function buildAntiForgetCalendar(
+  items: AntiForgetItem[],
+  month: Date,
+  selectedDate = '',
+  now = new Date(),
+): AntiForgetCalendarView {
+  const today = localDate(now);
+  const grouped = new Map<string, { total: number; completed: number }>();
+
+  for (const item of items) {
+    const date = antiForgetDate(item);
+    if (!date) continue;
+    const value = grouped.get(date) ?? { total: 0, completed: 0 };
+    value.total += 1;
+    if (antiForgetCompleted(item)) value.completed += 1;
+    grouped.set(date, value);
+  }
+
+  const monthStart = new Date(month.getFullYear(), month.getMonth(), 1, 12);
+  const gridStart = new Date(monthStart.getTime());
+  gridStart.setDate(gridStart.getDate() - ((gridStart.getDay() + 6) % 7));
+  const monthKey = `${monthStart.getFullYear()}-${String(monthStart.getMonth() + 1).padStart(2, '0')}`;
+  const days = Array.from({ length: 42 }, (_, index): AntiForgetCalendarDay => {
+    const dateValue = new Date(gridStart.getTime());
+    dateValue.setDate(gridStart.getDate() + index);
+    const date = localDate(dateValue);
+    const value = grouped.get(date) ?? { total: 0, completed: 0 };
+    const pending = Math.max(0, value.total - value.completed);
+    const state: AntiForgetCalendarState = !value.total
+      ? 'none'
+      : !pending
+        ? 'completed'
+        : date <= today
+          ? 'due'
+          : 'scheduled';
+    return {
+      date,
+      day: dateValue.getDate(),
+      inCurrentMonth: date.startsWith(monthKey),
+      isToday: date === today,
+      isSelected: date === selectedDate,
+      total: value.total,
+      completed: value.completed,
+      pending,
+      state,
+    };
+  });
+
+  return {
+    days,
+    monthLabel: `${monthStart.getFullYear()}年${monthStart.getMonth() + 1}月`,
+    scheduledDays: [...grouped.keys()].filter((date) => date.startsWith(monthKey)).length,
+  };
+}
+
+export function antiForgetTasksOnDate(items: AntiForgetItem[], date: string): AntiForgetItem[] {
+  return items.filter((item) => antiForgetDate(item) === date);
+}
+
+export function nearestAntiForgetDate(items: AntiForgetItem[], now = new Date()): string {
+  const today = localDate(now);
+  const dates = [...new Set(items.map(antiForgetDate).filter(Boolean))].sort();
+  if (!dates.length) return today;
+  if (dates.includes(today)) return today;
+
+  const pendingDates = dates.filter((date) => items.some((item) => antiForgetDate(item) === date && !antiForgetCompleted(item)));
+  const candidates = pendingDates.length ? pendingDates : dates;
+  return candidates.sort((left, right) => {
+    const leftDistance = Math.abs(Date.parse(left) - Date.parse(today));
+    const rightDistance = Math.abs(Date.parse(right) - Date.parse(today));
+    return leftDistance - rightDistance || left.localeCompare(right);
+  })[0];
+}
+
+export function antiForgetDate(item: AntiForgetItem): string {
+  return String(item['kywsj'] ?? item['fxrl'] ?? item['CreateTime'] ?? '').slice(0, 10);
+}
+
+export function antiForgetCompleted(item: AntiForgetItem): boolean {
+  return Number(item['fxzt'] ?? 0) === 1;
+}
+
+function localDate(date: Date): string {
+  const pad = (value: number) => String(value).padStart(2, '0');
+  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
+}
+
+function shiftDate(value: string, amount: number): string {
+  const [year, month, day] = value.split('-').map(Number);
+  const date = new Date(year, month - 1, day, 12);
+  date.setDate(date.getDate() + amount);
+  return localDate(date);
+}

+ 35 - 0
src/app/core/api.service.spec.ts

@@ -0,0 +1,35 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { API_CONFIG } from './app.constants';
+import { ApiError, ApiService } from './api.service';
+
+describe('ApiService', () => {
+  let api: ApiService;
+  let http: HttpTestingController;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()] });
+    api = TestBed.inject(ApiService);
+    http = TestBed.inject(HttpTestingController);
+  });
+
+  afterEach(() => http.verify());
+
+  it('uses the legacy action gateway and parses JSON result strings', () => {
+    let actual: unknown;
+    api.get('product_list', { cpage: 1 }).subscribe((response) => actual = response.result);
+    const request = http.expectOne((candidate) => candidate.url.includes(API_CONFIG.endpoint) && candidate.urlWithParams.includes('action=product_list'));
+    expect(request.request.params.get('cpage')).toBe('1');
+    request.flush({ retcode: 0, result: '[{"ID":1}]' });
+    expect(actual).toEqual([{ ID: 1 }]);
+  });
+
+  it('turns retcode -1 responses into ApiError', () => {
+    let actual: unknown;
+    api.post('user_login_passwd', {}).subscribe({ error: (error) => actual = error });
+    http.expectOne((candidate) => candidate.urlWithParams.includes('action=user_login_passwd')).flush({ retcode: -1, retmsg: '登录失败', result: null });
+    expect(actual instanceof ApiError).toBeTrue();
+    expect((actual as ApiError).message).toBe('登录失败');
+  });
+});

+ 94 - 0
src/app/core/api.service.ts

@@ -0,0 +1,94 @@
+import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
+import { inject, Injectable } from '@angular/core';
+import { catchError, map, Observable, throwError } from 'rxjs';
+import { API_CONFIG } from './app.constants';
+import { ApiEnvelope } from './models';
+
+export class ApiError extends Error {
+  constructor(message: string, readonly status = 0, readonly payload?: unknown) {
+    super(message);
+  }
+}
+
+@Injectable({ providedIn: 'root' })
+export class ApiService {
+  private readonly http = inject(HttpClient);
+
+  get<T>(action: string, params: Record<string, unknown> = {}): Observable<ApiEnvelope<T>> {
+    return this.http
+      .get<unknown>(this.actionUrl(action), { params: this.toHttpParams(params) })
+      .pipe(map((response) => this.normalize<T>(response)), catchError((error) => this.handleError(error)));
+  }
+
+  post<T>(action: string, body: Record<string, unknown> = {}): Observable<ApiEnvelope<T>> {
+    const encoded = this.toHttpParams(body);
+    return this.http
+      .post<unknown>(this.actionUrl(action), encoded.toString(), {
+        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+      })
+      .pipe(map((response) => this.normalize<T>(response)), catchError((error) => this.handleError(error)));
+  }
+
+  upload(file: File): Observable<ApiEnvelope<string>> {
+    const formData = new FormData();
+    formData.append('file', file, file.name);
+    return this.http
+      .post<unknown>(this.actionUrl('upload'), formData)
+      .pipe(map((response) => this.normalize<string>(response)), catchError((error) => this.handleError(error)));
+  }
+
+  actionUrl(action: string): string {
+    const query = new URLSearchParams({
+      apiId: API_CONFIG.apiId,
+      apiKey: API_CONFIG.apiKey,
+      action,
+    });
+    return `${API_CONFIG.baseUrl}${API_CONFIG.endpoint}?${query.toString()}`;
+  }
+
+  assetUrl(path: unknown): string {
+    const value = String(path ?? '').trim();
+    if (!value) return `${API_CONFIG.baseUrl}/UploadFiles/nopic.svg`;
+    if (/^(data:image|https?:\/\/)/i.test(value)) return value;
+    return `${API_CONFIG.baseUrl}${value.startsWith('/') ? value : `/${value}`}`;
+  }
+
+  private normalize<T>(input: unknown): ApiEnvelope<T> {
+    const envelope = (input ?? {}) as ApiEnvelope<unknown>;
+    let result = envelope.result;
+    if (typeof result === 'string') {
+      try {
+        result = JSON.parse(result) as unknown;
+      } catch {
+        // Some endpoints intentionally return plain text.
+      }
+    }
+    const normalized = { ...envelope, result } as ApiEnvelope<T>;
+    if (normalized.retcode === -1) {
+      throw new ApiError(normalized.retmsg || '请求失败', 200, normalized);
+    }
+    return normalized;
+  }
+
+  private toHttpParams(values: Record<string, unknown>): HttpParams {
+    let params = new HttpParams();
+    for (const [key, raw] of Object.entries(values)) {
+      if (raw === undefined || raw === null || raw === '') continue;
+      const value = typeof raw === 'object' ? JSON.stringify(raw) : String(raw);
+      params = params.set(key, value);
+    }
+    return params;
+  }
+
+  private handleError(error: unknown): Observable<never> {
+    if (error instanceof ApiError) return throwError(() => error);
+    if (error instanceof HttpErrorResponse) {
+      const payload = error.error as { retmsg?: string } | string | null;
+      const message = typeof payload === 'object' && payload?.retmsg
+        ? payload.retmsg
+        : error.message || '网络请求失败';
+      return throwError(() => new ApiError(message, error.status, payload));
+    }
+    return throwError(() => new ApiError(error instanceof Error ? error.message : '未知错误'));
+  }
+}

+ 27 - 0
src/app/core/app.constants.ts

@@ -0,0 +1,27 @@
+export const APP_NAME = '小树陪练';
+export const APP_VERSION = '2.0.0-angular';
+
+const isLocalDevelopment = ['localhost', '127.0.0.1'].includes(globalThis.location?.hostname ?? '');
+
+export const API_CONFIG = {
+  baseUrl: isLocalDevelopment ? '/backend' : 'https://test.fmode.cn/engxs',
+  endpoint: '/api/WXAPP',
+  apiId: '206a54b501ad6d7d8df0f78812e156c2',
+  apiKey: '41350aa1dcf7c9444841cdb153b4338d',
+} as const;
+
+export const BYTEDESK_CONFIG = {
+  apiBaseUrl: isLocalDevelopment ? '/bytedesk-api' : 'https://uniapp.bytedesk.com',
+  uploadBaseUrl: isLocalDevelopment ? '/bytedesk-upload' : 'https://upload.bytedesk.com',
+  subDomain: '202205251811281',
+  appKey: 'b0c9762b-5615-4a05-9412-1e0c4dd76ef8',
+  workGroupId: '202205251811323',
+  client: 'uniapp',
+} as const;
+
+export const SUCCESS_AUDIO = '/UploadFiles/ok.mp3';
+export const GAME_AUDIO = {
+  start: '/UploadFiles/0.mp3',
+  success: '/UploadFiles/1.mp3',
+  error: '/UploadFiles/2.mp3',
+} as const;

+ 11 - 0
src/app/core/auth.guard.ts

@@ -0,0 +1,11 @@
+import { inject } from '@angular/core';
+import { CanActivateFn, Router } from '@angular/router';
+import { SessionService } from './session.service';
+
+export const authGuard: CanActivateFn = (_route, state) => {
+  const session = inject(SessionService);
+  const router = inject(Router);
+  return session.isLoggedIn()
+    ? true
+    : router.createUrlTree(['/pages/member/wxauth'], { queryParams: { returnUrl: state.url } });
+};

+ 29 - 0
src/app/core/auth.service.ts

@@ -0,0 +1,29 @@
+import { inject, Injectable } from '@angular/core';
+import { map, Observable, switchMap, throwError } from 'rxjs';
+import { ApiError, ApiService } from './api.service';
+import { AppUser } from './models';
+
+@Injectable({ providedIn: 'root' })
+export class AuthService {
+  private readonly api = inject(ApiService);
+
+  login(action: 'user_login_passwd' | 'user_login_mobile', payload: Record<string, unknown>): Observable<AppUser> {
+    return this.api.post<AppUser>(action, payload).pipe(
+      switchMap(({ result }) => {
+        if (!result?.userId) return throwError(() => new ApiError('登录结果缺少会员信息'));
+        return this.api.post<AppUser>('user_get', { uid: result.userId }).pipe(
+          map(({ result: member, addon }) => {
+            const extra = this.asRecord(addon);
+            const state = Number(extra['State'] ?? extra['state'] ?? 0);
+            if (!state) throw new ApiError('您的账号正在审核中,请审核通过后再登录');
+            return { ...result, ...member, ...extra } as AppUser;
+          }),
+        );
+      }),
+    );
+  }
+
+  private asRecord(value: unknown): Record<string, unknown> {
+    return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
+  }
+}

+ 34 - 0
src/app/core/business-rules.spec.ts

@@ -0,0 +1,34 @@
+import { buildActionPayload, buildItemNavigation, buildLoadParams } from './business-rules';
+import { AppUser, PageAction, PageMeta } from './models';
+
+const user = { userId: 42, honeyName: '测试会员' } as AppUser;
+
+describe('source business rules', () => {
+  it('uses the selected coupon flow id', () => {
+    const meta = { path: 'pages/shop/coupon' } as PageMeta;
+    const action = { label: '领取', action: 'coupon_usrgot_add', scope: 'item' } as PageAction;
+    expect(buildActionPayload(meta, action, {}, user, {}, { ID: 3, Flow: 991 })).toEqual({ uid: 42, flowid: 991 });
+  });
+
+  it('uses the selected team member and source misspelled number field', () => {
+    const meta = { path: 'pages/member/group_detail' } as PageMeta;
+    const action = { label: '批发', action: 'user_group_usr_supply', scope: 'item' } as PageAction;
+    expect(buildActionPayload(meta, action, {}, user, { number: 12 }, { UserID: 88 })).toEqual({ uid: 42, upusr: 88, munber: 12 });
+  });
+
+  it('turns stock-out quantities negative', () => {
+    const meta = { path: 'pages/member/myshop_stock' } as PageMeta;
+    const action = { key: 'stock-out', label: '出库', action: 'product_stock_change' } as PageAction;
+    expect(buildActionPayload(meta, action, { id: 7 }, user, { amount: 5, remark: '发货' }, null)).toEqual({ uid: 42, proid: 7, num: -5, remark: '发货' });
+  });
+
+  it('maps source tab parameters without inventing numeric status values', () => {
+    const meta = { path: 'pages/member/mycoupon', tabParams: [{ fast: 'idle' }, { fast: 'used' }] } as unknown as PageMeta;
+    expect(buildLoadParams(meta, { action: 'coupon_usrgot_list' }, {}, user, 1)).toEqual({ cpage: 1, psize: 30, uid: 42, fast: 'used' });
+  });
+
+  it('preserves route-specific item query keys', () => {
+    const meta = { path: 'pages/shop/shop_nodes', itemRoute: '/pages/shop/shop_class' } as PageMeta;
+    expect(buildItemNavigation(meta, { NodeID: 19 })).toEqual({ path: '/pages/shop/shop_class', queryParams: { nid: 19 } });
+  });
+});

+ 212 - 0
src/app/core/business-rules.ts

@@ -0,0 +1,212 @@
+import { AppUser, ApiBinding, PageAction, PageMeta } from './models';
+
+export type BusinessItem = Record<string, unknown>;
+export type BusinessValues = Record<string, unknown>;
+
+export interface ItemNavigation {
+  path: string;
+  queryParams: Record<string, unknown>;
+}
+
+export function buildLoadParams(
+  meta: PageMeta,
+  binding: ApiBinding,
+  query: Record<string, unknown>,
+  user: AppUser | null,
+  activeTab = 0,
+): Record<string, unknown> {
+  const params: Record<string, unknown> = {
+    ...binding.params,
+    ...query,
+    cpage: query['cpage'] || 1,
+    psize: query['psize'] || 30,
+  };
+  if (user?.userId) params['uid'] = user.userId;
+  Object.assign(params, meta.tabParams?.[activeTab] ?? {});
+
+  for (const [target, source] of Object.entries(binding.queryMap ?? {})) {
+    const value = query[source];
+    if (value !== undefined && value !== null && value !== '') params[target] = value;
+  }
+
+  const id = query['id'] ?? query['sid'] ?? query['nid'];
+  if (id !== undefined) {
+    if (['content_get', 'order_get', 'product_get', 'invoice_get', 'receaddr_get', 'user_bank_get', 'user_rnauth_get'].includes(binding.action)) {
+      params['id'] = id;
+    }
+    if (binding.action === 'e_order_detail') params['id'] = query['id'] ?? query['sid'];
+  }
+
+  switch (meta.path) {
+    case 'pages/home/shop':
+      params['nid'] = 29;
+      params['myfield2'] = `pl=${user?.userId ?? 0}`;
+      params['dateFilter'] = localDate();
+      break;
+    case 'pages/home/cart':
+      params['nid'] ??= 0;
+      break;
+    case 'pages/shop/shop_class':
+      params['nid'] = query['nid'] ?? query['id'] ?? params['nid'];
+      break;
+    case 'pages/shop/shop_nodes':
+      params['pid'] = query['pid'] ?? 0;
+      break;
+    case 'pages/member/spread':
+    case 'pages/member/vip':
+    case 'pages/member/group':
+    case 'pages/member/group_detail':
+      params['gid'] = query['gid'] ?? query['id'] ?? params['gid'];
+      break;
+    case 'pages/member/myshop':
+      params['uids'] = user?.userId;
+      delete params['uid'];
+      break;
+    case 'pages/member/myshop_stock':
+      params['proid'] = query['id'];
+      break;
+    case 'pages/member/myshop_sales_log':
+      params['pubid'] = 13;
+      break;
+    case 'pages/shop/shop_item_comment':
+      params['proId'] = query['id'];
+      params['rate'] = query['rate'] ?? 'all';
+      params['hasImgOnly'] = query['hasImgOnly'] ?? 0;
+      break;
+    case 'pages/shop/order_comment':
+      params['orderId'] = query['id'];
+      params['userId'] = user?.userId;
+      break;
+  }
+
+  return compact(params);
+}
+
+export function buildActionPayload(
+  meta: PageMeta,
+  action: PageAction,
+  query: Record<string, unknown>,
+  user: AppUser | null,
+  values: BusinessValues,
+  item: BusinessItem | null,
+): Record<string, unknown> {
+  const uid = user?.userId;
+  const id = itemId(item) ?? query['id'];
+  const payload: Record<string, unknown> = {
+    ...action.params,
+    ...query,
+    ...values,
+    uid,
+  };
+
+  if (action.scope === 'item' && id !== null) payload['id'] = id;
+
+  switch (action.action) {
+    case 'coupon_usrgot_add':
+      return compact({ uid, flowid: item?.['Flow'] ?? item?.['flow'] ?? id });
+    case 'product_del':
+    case 'receaddr_del':
+    case 'invoice_del':
+    case 'user_bank_del':
+      return compact({ uid, id });
+    case 'product_sale_change':
+      return compact({ uid, id, status: action.params?.['status'] });
+    case 'user_group_usr_upgrade':
+      return compact({ uid, upusr: item?.['UserID'] ?? item?.['userId'] ?? id, gid: values['gid'] });
+    case 'user_group_usr_supply':
+      return compact({ uid, upusr: item?.['UserID'] ?? item?.['userId'] ?? id, munber: values['number'] ?? values['munber'] });
+    case 'product_stock_change': {
+      const amount = Math.abs(Number(values['amount'] ?? values['num'] ?? 0));
+      return compact({ uid, proid: query['id'] ?? id, num: action.key === 'stock-out' ? -amount : amount, remark: values['remark'] });
+    }
+    case 'order_delivery':
+      return compact({ uid, id, expComp: values['expComp'] ?? values['expName'], expNo: values['expNo'], expRemind: values['expRemind'] });
+    case 'order_signfor':
+      return compact({ uid, id });
+    case 'user_exp_transfer':
+      return compact({ uid, touser: values['touser'] ?? values['mobile'], money: values['money'] ?? values['amount'], remark: values['remark'] });
+    case 'user_money_transfer':
+      return compact({ uid, touser: values['touser'] ?? values['mobile'], money: values['money'] ?? values['amount'], remark: values['remark'], passwd: values['passwd'] });
+    case 'user_money_recharge':
+      return compact({ appId: 3, uid, money: values['money'] ?? values['amount'], use_alipay: values['provider'] === 'alipay' ? 1 : 0 });
+    case 'user_cash_add':
+      return compact({ uid, money: values['money'] ?? values['amount'], bankid: values['bankid'], passwd: values['passwd'] });
+    case 'user_coin_recharge':
+      return compact({ uid, money: values['money'] ?? values['amount'] });
+    case 'pub_add':
+      if (meta.path === 'pages/member/myshop_sales') {
+        return compact({
+          uid,
+          pubid: 13,
+          model: JSON.stringify({
+            PubTitle: `${user?.honeyName ?? user?.userName ?? '会员'}的结算申请`,
+            PubContent: values['remarks'],
+            jsje: values['money'] ?? values['amount'],
+            PubIsTrue: 1,
+          }),
+        });
+      }
+      break;
+    case 'e_order_update_v2':
+      return compact({
+        uid,
+        status: action.params?.['status'] ?? values['status'],
+        content: JSON.stringify({ GeneralID: query['id'] ?? query['sid'] ?? id, UpDateTime: localDateTime() }),
+      });
+  }
+
+  return compact(payload);
+}
+
+export function actionMatches(action: PageAction, item: BusinessItem): boolean {
+  if (!action.when) return true;
+  return Object.entries(action.when).every(([key, expected]) => String(item[key] ?? '') === String(expected));
+}
+
+export function buildItemNavigation(meta: PageMeta, item: BusinessItem): ItemNavigation | null {
+  if (!meta.itemRoute) return null;
+  const id = itemId(item);
+  const queryParams: Record<string, unknown> = {};
+
+  switch (meta.itemRoute) {
+    case '/pages/shop/shop_class':
+    case '/pages/stu/stu_list':
+      queryParams['nid'] = item['NodeID'] ?? item['Nodeid'] ?? id;
+      break;
+    case '/pages/stu/stu_abc':
+      queryParams['nid'] = item['NodeID'] ?? id;
+      queryParams['nids'] = item['NodeID'] ?? id;
+      break;
+    case '/pages/his/his':
+      queryParams['uid'] = item['szyh'] ?? item['UserID'] ?? item['userId'] ?? id;
+      queryParams['name'] = item['honeyname'] ?? item['HoneyName'] ?? item['honeyName'];
+      break;
+    default:
+      if (id !== null) queryParams['id'] = id;
+  }
+
+  if (meta.path === 'pages/member/ke_xiao' && Number(item['dszt'] ?? 0) > 10) {
+    return { path: '/pages/shop/baobiao', queryParams: { sid: item['GeneralID'] ?? id } };
+  }
+  return { path: meta.itemRoute, queryParams: compact(queryParams) };
+}
+
+export function itemId(item: BusinessItem | null): string | number | null {
+  if (!item) return null;
+  const value = item['ID'] ?? item['id'] ?? item['GeneralID'] ?? item['OrderID'] ?? item['UserID'] ?? item['userId'] ?? item['Flow'];
+  return typeof value === 'string' || typeof value === 'number' ? value : null;
+}
+
+function compact(values: Record<string, unknown>): Record<string, unknown> {
+  return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined && value !== null && value !== ''));
+}
+
+function localDate(): string {
+  const now = new Date();
+  return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
+}
+
+function localDateTime(): string {
+  const now = new Date();
+  return `${localDate()} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
+}

+ 108 - 0
src/app/core/bytedesk.service.spec.ts

@@ -0,0 +1,108 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { BYTEDESK_CONFIG } from './app.constants';
+import { BytedeskChatSession, BytedeskService } from './bytedesk.service';
+
+describe('BytedeskService', () => {
+  let service: BytedeskService;
+  let http: HttpTestingController;
+
+  const visitor = { uid: 'visitor-1', username: 'guest-1', nickname: '访客', avatar: '', accessToken: 'token-1' };
+  const session: BytedeskChatSession = {
+    visitor,
+    thread: { tid: 'thread-1', topic: 'topic/1', type: 'workGroup' },
+    initialMessage: {},
+    statusCode: 200,
+    isRobot: false,
+  };
+
+  beforeEach(() => {
+    localStorage.removeItem('xiaoshu.bytedesk.session');
+    TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()] });
+    service = TestBed.inject(BytedeskService);
+    http = TestBed.inject(HttpTestingController);
+  });
+
+  afterEach(() => {
+    http.verify();
+    localStorage.removeItem('xiaoshu.bytedesk.session');
+  });
+
+  it('registers an anonymous visitor, logs in, and requests the original workgroup', () => {
+    let actual: BytedeskChatSession | undefined;
+    service.connect().subscribe((value) => actual = value);
+
+    const register = http.expectOne((request) => request.url === `${BYTEDESK_CONFIG.apiBaseUrl}/visitor/api/username`);
+    expect(register.request.params.get('subDomain')).toBe(BYTEDESK_CONFIG.subDomain);
+    register.flush({ status_code: 200, data: { uid: 'visitor-1', username: 'guest-1', nickname: '访客', avatar: '' } });
+
+    const login = http.expectOne(`${BYTEDESK_CONFIG.apiBaseUrl}/oauth/token`);
+    expect(login.request.headers.get('Authorization')).toContain('Basic ');
+    expect(login.request.body).toContain('username=guest-1');
+    login.flush({ access_token: 'token-1' });
+
+    const thread = http.expectOne((request) => request.url === `${BYTEDESK_CONFIG.apiBaseUrl}/api/thread/request`);
+    expect(thread.request.params.get('wId')).toBe(BYTEDESK_CONFIG.workGroupId);
+    expect(thread.request.headers.get('Authorization')).toBe('Bearer token-1');
+    thread.flush({ status_code: 206, data: { mid: 'welcome', thread: { tid: 'thread-1', topic: 'topic/1', type: 'workGroup' } } });
+
+    expect(actual?.isRobot).toBeTrue();
+    expect(actual?.thread.topic).toBe('topic/1');
+  });
+
+  it('uploads an image and sends the returned URL as a Bytedesk message', () => {
+    const file = new File(['image'], 'sample.png', { type: 'image/png' });
+    let sentType = '';
+    service.sendAttachment(session, file).subscribe((message) => sentType = String(message.type));
+
+    const upload = http.expectOne(`${BYTEDESK_CONFIG.uploadBaseUrl}/visitor/api/upload/image`);
+    expect(upload.request.body instanceof FormData).toBeTrue();
+    expect((upload.request.body as FormData).get('username')).toBe('guest-1');
+    upload.flush({ status_code: 200, data: 'https://upload.bytedesk.com/sample.png' });
+
+    const send = http.expectOne(`${BYTEDESK_CONFIG.apiBaseUrl}/api/messages/send`);
+    const message = JSON.parse(String((send.request.body as Record<string, unknown>)['json'])) as { type: string; image: { imageUrl: string } };
+    expect(message.type).toBe('image');
+    expect(message.image.imageUrl).toContain('sample.png');
+    send.flush({ status_code: 200, data: null });
+    expect(sentType).toBe('image');
+  });
+
+  it('uses the original robot answer endpoint for robot sessions', () => {
+    let messages = 0;
+    service.sendText({ ...session, isRobot: true, statusCode: 206 }, '怎么学习').subscribe((value) => messages = value.length);
+    const answer = http.expectOne((request) => request.url === `${BYTEDESK_CONFIG.apiBaseUrl}/api/v2/answer/message`);
+    expect(answer.request.params.get('content')).toBe('怎么学习');
+    answer.flush({ status_code: 200, data: { reply: { mid: 'reply-1', text: { content: '请先选择课程' } } } });
+    expect(messages).toBe(2);
+  });
+
+  it('loads and submits ratings through the original Bytedesk endpoints', () => {
+    localStorage.setItem('xiaoshu.bytedesk.session', JSON.stringify({ ...visitor, subDomain: BYTEDESK_CONFIG.subDomain }));
+    let rated = false;
+    service.rateDetail('thread-1').subscribe();
+    const detail = http.expectOne((request) => request.url === `${BYTEDESK_CONFIG.apiBaseUrl}/api/rate/detail`);
+    expect(detail.request.params.get('tid')).toBe('thread-1');
+    expect(detail.request.headers.get('Authorization')).toBe('Bearer token-1');
+    detail.flush({ status_code: 200, data: { rated: false, agent: { nickname: '客服' } } });
+
+    service.submitRate('thread-1', 5, '问题已解决', true).subscribe(() => rated = true);
+    const submit = http.expectOne(`${BYTEDESK_CONFIG.apiBaseUrl}/api/rate/do`);
+    expect(submit.request.body).toEqual(jasmine.objectContaining({ tid: 'thread-1', score: 5, note: '问题已解决', invite: true }));
+    expect(submit.request.headers.get('Authorization')).toBe('Bearer token-1');
+    submit.flush({ status_code: 200, data: null });
+    expect(rated).toBeTrue();
+  });
+
+  it('submits offline messages with the original conversation context', () => {
+    localStorage.setItem('xiaoshu.bytedesk.session', JSON.stringify({ ...visitor, subDomain: BYTEDESK_CONFIG.subDomain }));
+    service.saveLeaveMessage({ wid: 'workgroup-1', aid: 'agent-1', type: 'workGroup', mobile: '13800000000', email: 'test@example.com', content: '请联系我' }).subscribe();
+    const request = http.expectOne(`${BYTEDESK_CONFIG.apiBaseUrl}/api/leavemsg/save`);
+    expect(request.request.body).toEqual(jasmine.objectContaining({
+      wid: 'workgroup-1', aid: 'agent-1', type: 'workGroup', mobile: '13800000000', email: 'test@example.com', content: '请联系我', client: BYTEDESK_CONFIG.client,
+    }));
+    expect(request.request.headers.get('Authorization')).toBe('Bearer token-1');
+    request.flush({ status_code: 200, data: null });
+  });
+});

+ 322 - 0
src/app/core/bytedesk.service.ts

@@ -0,0 +1,322 @@
+import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
+import { inject, Injectable } from '@angular/core';
+import { catchError, map, Observable, of, switchMap, throwError } from 'rxjs';
+import { BYTEDESK_CONFIG } from './app.constants';
+
+export interface BytedeskVisitor {
+  uid: string;
+  username: string;
+  nickname: string;
+  avatar: string;
+  accessToken: string;
+}
+
+export interface BytedeskThread {
+  tid: string;
+  topic: string;
+  type: string;
+  visitor?: Record<string, unknown>;
+  [key: string]: unknown;
+}
+
+export interface BytedeskMessage {
+  mid?: string;
+  timestamp?: string;
+  createdAt?: string;
+  type?: string;
+  content?: string;
+  status?: string;
+  user?: Record<string, unknown>;
+  thread?: BytedeskThread;
+  text?: { content?: string };
+  image?: { imageUrl?: string };
+  file?: { fileUrl?: string };
+  imageUrl?: string;
+  fileUrl?: string;
+  [key: string]: unknown;
+}
+
+export interface BytedeskChatSession {
+  visitor: BytedeskVisitor;
+  thread: BytedeskThread;
+  initialMessage: BytedeskMessage;
+  statusCode: number;
+  isRobot: boolean;
+}
+
+interface BytedeskResponse<T> {
+  status_code?: number;
+  message?: string;
+  data: T;
+}
+
+interface StoredBytedeskSession extends BytedeskVisitor {
+  subDomain: string;
+}
+
+const STORAGE_KEY = 'xiaoshu.bytedesk.session';
+const BASIC_AUTH = 'Basic Y2xpZW50OnNlY3JldA==';
+
+export class BytedeskError extends Error {
+  constructor(message: string, readonly status = 0, readonly payload?: unknown) { super(message); }
+}
+
+@Injectable({ providedIn: 'root' })
+export class BytedeskService {
+  private readonly http = inject(HttpClient);
+
+  connect(): Observable<BytedeskChatSession> {
+    const stored = this.restore();
+    if (!stored) return this.registerAndLogin().pipe(switchMap((visitor) => this.requestThread(visitor)));
+    return this.requestThread(stored).pipe(
+      catchError(() => this.login(stored).pipe(switchMap((visitor) => this.requestThread(visitor)))),
+    );
+  }
+
+  history(session: BytedeskChatSession, size = 30): Observable<BytedeskMessage[]> {
+    const params = new HttpParams()
+      .set('topic', session.thread.topic)
+      .set('page', 0)
+      .set('size', size)
+      .set('client', BYTEDESK_CONFIG.client);
+    return this.http.get<BytedeskResponse<{ content?: BytedeskMessage[] }>>(`${BYTEDESK_CONFIG.apiBaseUrl}/api/messages/topic`, {
+      params,
+      headers: this.authHeaders(session.visitor),
+    }).pipe(
+      map((response) => response.status_code === 200 && Array.isArray(response.data?.content) ? response.data.content : []),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  sendText(session: BytedeskChatSession, content: string): Observable<BytedeskMessage[]> {
+    const queryMessage = this.makeMessage(session, 'text', content);
+    if (session.isRobot) {
+      const params = new HttpParams()
+        .set('wid', BYTEDESK_CONFIG.workGroupId)
+        .set('content', content)
+        .set('client', BYTEDESK_CONFIG.client);
+      return this.http.get<BytedeskResponse<{ reply?: BytedeskMessage }>>(`${BYTEDESK_CONFIG.apiBaseUrl}/api/v2/answer/message`, {
+        params,
+        headers: this.authHeaders(session.visitor),
+      }).pipe(
+        map((response) => {
+          if (![200, 201].includes(Number(response.status_code))) throw new BytedeskError(response.message || '智能客服回复失败', Number(response.status_code), response);
+          const reply = response.data?.reply;
+          return reply ? [queryMessage, { ...reply, type: 'robot_result' }] : [queryMessage];
+        }),
+        catchError((error) => this.handleError(error)),
+      );
+    }
+    return this.sendMessage(session, queryMessage).pipe(map(() => [queryMessage]));
+  }
+
+  sendAttachment(session: BytedeskChatSession, file: File): Observable<BytedeskMessage> {
+    const type = file.type.startsWith('image/') ? 'image' : 'file';
+    return this.upload(session.visitor, file, type).pipe(
+      switchMap((url) => {
+        const message = this.makeMessage(session, type, url);
+        message['fileName'] = file.name;
+        return this.sendMessage(session, message).pipe(map(() => message));
+      }),
+    );
+  }
+
+  rateDetail(tid: string): Observable<Record<string, unknown>> {
+    return this.visitor().pipe(
+      switchMap((visitor) => this.http.get<BytedeskResponse<Record<string, unknown>>>(`${BYTEDESK_CONFIG.apiBaseUrl}/api/rate/detail`, {
+        params: new HttpParams().set('tid', tid).set('client', BYTEDESK_CONFIG.client),
+        headers: this.authHeaders(visitor),
+      })),
+      map((response) => {
+        if (Number(response.status_code) !== 200) throw new BytedeskError(response.message || '评价信息加载失败', Number(response.status_code), response);
+        return response.data ?? {};
+      }),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  submitRate(tid: string, score: number, note: string, invite: boolean): Observable<void> {
+    return this.visitor().pipe(
+      switchMap((visitor) => this.http.post<BytedeskResponse<unknown>>(`${BYTEDESK_CONFIG.apiBaseUrl}/api/rate/do`, {
+        tid, score, note, invite, client: BYTEDESK_CONFIG.client,
+      }, { headers: this.authHeaders(visitor) })),
+      map((response) => {
+        if (Number(response.status_code) !== 200) throw new BytedeskError(response.message || '提交评价失败', Number(response.status_code), response);
+      }),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  saveLeaveMessage(values: { wid: string; aid: string; type: string; mobile: string; email: string; content: string }): Observable<void> {
+    return this.visitor().pipe(
+      switchMap((visitor) => this.http.post<BytedeskResponse<unknown>>(`${BYTEDESK_CONFIG.apiBaseUrl}/api/leavemsg/save`, {
+        ...values, client: BYTEDESK_CONFIG.client,
+      }, { headers: this.authHeaders(visitor) })),
+      map((response) => {
+        if (Number(response.status_code) !== 200) throw new BytedeskError(response.message || '留言失败', Number(response.status_code), response);
+      }),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  private visitor(): Observable<BytedeskVisitor> {
+    const stored = this.restore();
+    return stored ? of(stored) : this.registerAndLogin();
+  }
+
+  private registerAndLogin(): Observable<BytedeskVisitor> {
+    const params = new HttpParams().set('subDomain', BYTEDESK_CONFIG.subDomain).set('client', BYTEDESK_CONFIG.client);
+    return this.http.get<BytedeskResponse<Record<string, unknown>>>(`${BYTEDESK_CONFIG.apiBaseUrl}/visitor/api/username`, { params }).pipe(
+      map((response) => this.readVisitor(response.data)),
+      switchMap((visitor) => this.login(visitor)),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  private login(visitor: Omit<BytedeskVisitor, 'accessToken'> | BytedeskVisitor): Observable<BytedeskVisitor> {
+    const body = new HttpParams()
+      .set('username', visitor.username)
+      .set('password', visitor.username)
+      .set('grant_type', 'password')
+      .set('scope', 'all');
+    return this.http.post<{ access_token?: string }>(`${BYTEDESK_CONFIG.apiBaseUrl}/oauth/token`, body.toString(), {
+      headers: new HttpHeaders({ Authorization: BASIC_AUTH, 'Content-Type': 'application/x-www-form-urlencoded' }),
+    }).pipe(
+      map((response) => {
+        if (!response.access_token) throw new BytedeskError('客服登录失败', 0, response);
+        const authenticated = { ...visitor, accessToken: response.access_token } as BytedeskVisitor;
+        this.persist(authenticated);
+        return authenticated;
+      }),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  private requestThread(visitor: BytedeskVisitor): Observable<BytedeskChatSession> {
+    const params = new HttpParams()
+      .set('wId', BYTEDESK_CONFIG.workGroupId)
+      .set('type', 'workGroup')
+      .set('aId', '')
+      .set('client', BYTEDESK_CONFIG.client);
+    return this.http.get<BytedeskResponse<BytedeskMessage>>(`${BYTEDESK_CONFIG.apiBaseUrl}/api/thread/request`, {
+      params,
+      headers: this.authHeaders(visitor),
+    }).pipe(
+      map((response) => {
+        const statusCode = Number(response.status_code);
+        const thread = response.data?.thread;
+        if (![200, 201, 202, 203, 204, 205, 206].includes(statusCode) || !thread?.topic) {
+          throw new BytedeskError(response.message || '无法建立客服会话', statusCode, response);
+        }
+        return { visitor, thread, initialMessage: response.data, statusCode, isRobot: statusCode === 206 };
+      }),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  private sendMessage(session: BytedeskChatSession, message: BytedeskMessage): Observable<void> {
+    return this.http.post<unknown>(`${BYTEDESK_CONFIG.apiBaseUrl}/api/messages/send`, {
+      json: JSON.stringify(message),
+      client: BYTEDESK_CONFIG.client,
+    }, { headers: this.authHeaders(session.visitor) }).pipe(
+      map(() => undefined),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  private upload(visitor: BytedeskVisitor, file: File, type: 'image' | 'file'): Observable<string> {
+    const data = new FormData();
+    data.append('file', file, file.name);
+    data.append('file_name', `${this.guid()}-${file.name}`);
+    data.append('username', visitor.username);
+    data.append('client', BYTEDESK_CONFIG.client);
+    return this.http.post<BytedeskResponse<string>>(`${BYTEDESK_CONFIG.uploadBaseUrl}/visitor/api/upload/${type}`, data).pipe(
+      map((response) => {
+        if (!response.data || typeof response.data !== 'string') throw new BytedeskError('附件上传失败', Number(response.status_code), response);
+        return response.data;
+      }),
+      catchError((error) => this.handleError(error)),
+    );
+  }
+
+  private makeMessage(session: BytedeskChatSession, type: 'text' | 'image' | 'file', value: string): BytedeskMessage {
+    const timestamp = this.timestamp();
+    const content = type === 'text' ? value : type === 'image' ? '[图片]' : '[文件]';
+    const message: BytedeskMessage = {
+      mid: this.guid(), timestamp, client: BYTEDESK_CONFIG.client, version: '1', type, status: 'sending',
+      user: {
+        uid: session.visitor.uid,
+        username: session.visitor.username,
+        nickname: session.visitor.nickname,
+        avatar: session.visitor.avatar,
+        extra: { agent: false },
+      },
+      thread: {
+        ...session.thread,
+        content,
+        nickname: session.visitor.nickname,
+        avatar: session.visitor.avatar,
+        client: BYTEDESK_CONFIG.client,
+        timestamp,
+        unreadCount: 0,
+      },
+    };
+    if (type === 'text') message.text = { content: value };
+    if (type === 'image') message.image = { imageUrl: value };
+    if (type === 'file') message.file = { fileUrl: value };
+    return message;
+  }
+
+  private readVisitor(value: Record<string, unknown>): Omit<BytedeskVisitor, 'accessToken'> {
+    const username = String(value?.['username'] ?? '');
+    const uid = String(value?.['uid'] ?? '');
+    if (!username || !uid) throw new BytedeskError('无法创建客服访客', 0, value);
+    return {
+      uid,
+      username,
+      nickname: String(value?.['nickname'] ?? '访客'),
+      avatar: String(value?.['avatar'] ?? ''),
+    };
+  }
+
+  private authHeaders(visitor: BytedeskVisitor): HttpHeaders {
+    return new HttpHeaders({ Authorization: `Bearer ${visitor.accessToken}`, 'Content-Type': 'application/json' });
+  }
+
+  private persist(visitor: BytedeskVisitor): void {
+    const value: StoredBytedeskSession = { ...visitor, subDomain: BYTEDESK_CONFIG.subDomain };
+    localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
+  }
+
+  private restore(): BytedeskVisitor | null {
+    try {
+      const value = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null') as StoredBytedeskSession | null;
+      if (!value?.uid || !value.username || !value.accessToken || value.subDomain !== BYTEDESK_CONFIG.subDomain) return null;
+      return value;
+    } catch {
+      return null;
+    }
+  }
+
+  private timestamp(): string {
+    const date = new Date();
+    const pad = (value: number) => String(value).padStart(2, '0');
+    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
+  }
+
+  private guid(): string {
+    return `${Date.now()}-${globalThis.crypto?.randomUUID?.() ?? Math.random().toString(16).slice(2)}`;
+  }
+
+  private handleError(error: unknown): Observable<never> {
+    if (error instanceof BytedeskError) return throwError(() => error);
+    if (error instanceof HttpErrorResponse) {
+      const payload = error.error as { message?: string; error_description?: string } | null;
+      if (error.status === 200 && /parsing/i.test(error.message)) {
+        return throwError(() => new BytedeskError('原 Bytedesk 客服接口已停止服务,需要配置新版微语客服', 200, payload));
+      }
+      return throwError(() => new BytedeskError(payload?.message || payload?.error_description || error.message || '客服网络请求失败', error.status, payload));
+    }
+    return throwError(() => new BytedeskError(error instanceof Error ? error.message : '客服请求失败'));
+  }
+}

+ 22 - 0
src/app/core/external-reading.spec.ts

@@ -0,0 +1,22 @@
+import { extractReadingWords, readNcloudSessionToken } from './external-reading';
+import { normalizeReadingParams } from '../features/english-reading/reading.utils';
+
+describe('external reading migration', () => {
+  it('extracts source Title values from serialized xxqs data', () => {
+    expect(extractReadingWords('[{"Title":"apple"},{"Title":" book "}]')).toEqual(['apple', 'book']);
+    expect(extractReadingWords('{"0":{"Title":"cat"}}')).toEqual(['cat']);
+    expect(extractReadingWords('not-json')).toEqual([]);
+  });
+
+  it('reads the original NCloud cache key without failing on invalid data', () => {
+    const storage = { getItem: () => JSON.stringify({ sessionToken: 'session-123' }) };
+    expect(readNcloudSessionToken(storage)).toBe('session-123');
+    expect(readNcloudSessionToken({ getItem: () => '{' })).toBe('');
+  });
+
+  it('accepts packed legacy params in the internal reading route', () => {
+    const value = JSON.stringify({ wordList: ['one'], token: 'token' });
+    expect(normalizeReadingParams({ params: value }).wordList).toEqual(['one']);
+    expect(normalizeReadingParams({ params: encodeURIComponent(value) }).token).toBe('token');
+  });
+});

+ 22 - 0
src/app/core/external-reading.ts

@@ -0,0 +1,22 @@
+export function extractReadingWords(value: unknown): string[] {
+  let parsed = value;
+  if (typeof value === 'string') {
+    try { parsed = JSON.parse(value) as unknown; }
+    catch { return []; }
+  }
+  const entries = Array.isArray(parsed)
+    ? parsed
+    : parsed && typeof parsed === 'object' ? Object.values(parsed) : [];
+  return entries
+    .map((entry) => entry && typeof entry === 'object' ? String((entry as Record<string, unknown>)['Title'] ?? '').trim() : '')
+    .filter(Boolean);
+}
+
+export function readNcloudSessionToken(storage: Pick<Storage, 'getItem'> = localStorage): string {
+  try {
+    const cached = JSON.parse(storage.getItem('NCloud/dev/User') || '{}') as { sessionToken?: unknown };
+    return typeof cached.sessionToken === 'string' ? cached.sessionToken : '';
+  } catch {
+    return '';
+  }
+}

+ 42 - 0
src/app/core/form-payload.spec.ts

@@ -0,0 +1,42 @@
+import { buildFormSubmission } from './form-payload';
+import { PAGE_BY_PATH } from './page-catalog';
+
+describe('form payload adapters', () => {
+  it('maps the address form to the legacy address API contract', () => {
+    const meta = PAGE_BY_PATH.get('pages/member/receaddr_add')!;
+    const submission = buildFormSubmission(meta, {
+      ReceivName: '测试用户',
+      MobileNum: '13800138000',
+      Provinces: '江西省 南昌市 红谷滩区',
+      Street: '测试路 1 号',
+      Zipcode: '330000',
+    }, { uid: 7, id: '9' });
+
+    expect(submission?.action).toBe('receaddr_upd');
+    expect(submission?.payload).toEqual(jasmine.objectContaining({
+      uid: 7,
+      id: '9',
+      provinceName: '江西省',
+      cityName: '南昌市',
+      countyName: '红谷滩区',
+      userName: '测试用户',
+      telNumber: '13800138000',
+    }));
+  });
+
+  it('serializes product model and gallery data for the product API', () => {
+    const meta = PAGE_BY_PATH.get('pages/member/myshop_add')!;
+    const submission = buildFormSubmission(meta, { Proname: '测试商品', Nodeid: 2, pics: '/upload/pic.jpg' }, { uid: 7 });
+
+    expect(submission?.action).toBe('product_add');
+    expect(JSON.parse(String(submission?.payload['model']))).toEqual(jasmine.objectContaining({ Proname: '测试商品', Nodeid: 2 }));
+    expect(JSON.parse(JSON.parse(String(submission?.payload['addon'])).pics)[0].url).toBe('/upload/pic.jpg');
+  });
+
+  it('maps password confirmation fields to the original API names', () => {
+    const meta = PAGE_BY_PATH.get('pages/member/passwd')!;
+    const submission = buildFormSubmission(meta, { passwd: 'secret123', passwd2: 'secret123', vcode: '123456' }, { uid: 7 });
+
+    expect(submission?.payload).toEqual(jasmine.objectContaining({ newPass: 'secret123', reNewPass: 'secret123', vcode: '123456' }));
+  });
+});

+ 210 - 0
src/app/core/form-payload.ts

@@ -0,0 +1,210 @@
+import { AppUser, PageMeta } from './models';
+
+export type FormValue = string | number | boolean | null;
+export type FormValues = Record<string, FormValue>;
+
+export interface FormSubmissionContext {
+  uid?: number;
+  id?: string;
+  user?: AppUser | null;
+  loaded?: Record<string, unknown>;
+  addon?: unknown;
+}
+
+export interface FormSubmission {
+  action: string;
+  payload: Record<string, unknown>;
+}
+
+const image = (url: FormValue, name = '') => ({ url: String(url ?? ''), name, desc: '', link: '' });
+const asObject = (value: unknown): Record<string, unknown> => value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
+const gallery = (value: FormValue): Array<{ url: string; name: string; desc: string; link: string }> => {
+  const raw = String(value ?? '').trim();
+  if (!raw) return [];
+  try {
+    const parsed = JSON.parse(raw) as Array<{ url?: string; name?: string; desc?: string; link?: string }>;
+    if (Array.isArray(parsed)) return parsed.filter((item) => item?.url).map((item) => ({ url: String(item.url), name: item.name ?? '', desc: item.desc ?? '', link: item.link ?? '' }));
+  } catch { /* A single uploaded path is also valid. */ }
+  return [image(raw)];
+};
+
+export function buildFormSubmission(meta: PageMeta, values: FormValues, context: FormSubmissionContext): FormSubmission | null {
+  const loaded = context.loaded ?? {};
+  const recordId = context.id || String(loaded['ID'] ?? loaded['id'] ?? '') || undefined;
+  const saveAction = resolveSaveAction(meta.saveAction, Boolean(recordId));
+  if (!saveAction) return null;
+
+  const uid = context.uid;
+  const id = recordId;
+  const user = context.user;
+  let action = saveAction;
+  let payload: Record<string, unknown> = { id, ...values, uid };
+
+  switch (meta.path) {
+    case 'components/bytedesk_kefu/rate':
+      payload = {
+        model: JSON.stringify({ UserID: uid, Title: '客服满意度评价', TContent: values['content'], Score: values['score'], Cateid: 22 }),
+      };
+      break;
+    case 'components/bytedesk_kefu/leavemsg':
+      payload = {
+        model: JSON.stringify({ UserID: uid, Title: `客服留言 ${values['mobile'] ?? ''}`, TContent: values['content'], Cateid: 22 }),
+      };
+      break;
+    case 'pages/shop/order_appeal': {
+      const pubid = Number(values['type'] || 10);
+      const orderId = loaded['id'] ?? loaded['ID'] ?? id;
+      const pictures = gallery(values['images']);
+      payload = {
+        uid,
+        pubid,
+        model: JSON.stringify({
+          PubTitle: `${user?.honeyName || user?.userName || '会员'}的${pubid === 10 ? '退货' : '换货'}申请`,
+          OrderNo: loaded['OrderNo'] ?? '',
+          Concat: values['concat'],
+          PubContent: values['reason'],
+          Pics: JSON.stringify(pictures),
+          PubIsTrue: 1,
+          PubContentid: orderId,
+        }),
+      };
+      break;
+    }
+    case 'pages/shop/order_comment_add': {
+      const addon = asObject(context.addon);
+      const carts = Array.isArray(addon['cart']) ? addon['cart'] as Record<string, unknown>[] : [];
+      const product = carts.find((item) => String(item['ProID']) === String(values['proId'])) ?? carts[0] ?? {};
+      const pictures = gallery(values['images']);
+      payload = {
+        model: JSON.stringify({
+          UserID: uid,
+          Title: values['title'],
+          Score: values['score'],
+          MsgContent: values['content'],
+          Imgs: pictures.map((item) => item.url).join('|'),
+          IsAnonymous: values['anonymous'] ? 1 : 0,
+          OrderID: loaded['id'] ?? loaded['ID'] ?? id,
+          ProID: product['ProID'] ?? values['proId'],
+          OrderDate: loaded['AddTime'] ?? '',
+        }),
+      };
+      break;
+    }
+    case 'pages/member/settings':
+      payload = { uid, mu: JSON.stringify({ ...user, ...values }), inviCode: values['inviCode'] ?? '' };
+      break;
+    case 'pages/member/wxauth_after':
+      payload = { uid, login: values['passwd'], pay: values['paypwd'] };
+      break;
+    case 'pages/member/receaddr_add': {
+      const [provinceName = '', cityName = '', countyName = ''] = String(values['Provinces'] ?? '').trim().split(/\s+/);
+      payload = {
+        uid,
+        id,
+        provinceName,
+        cityName,
+        countyName,
+        userName: values['ReceivName'],
+        telNumber: values['MobileNum'],
+        detailInfo: values['Street'],
+        postalCode: values['Zipcode'],
+      };
+      break;
+    }
+    case 'pages/member/invoice_add':
+      payload = {
+        uid,
+        id,
+        aud: values['InvType'] ?? 0,
+        name: values['InvHead'],
+        idnum: values['UserCode'],
+        mobile: values['UserMobile'],
+        email: values['UserEmail'],
+        address: values['UserAddress'],
+        bankname: values['UserBankName'],
+        bankusr: values['UserBankAccount'],
+      };
+      break;
+    case 'pages/member/acc_destory':
+      payload = {
+        pubid: 7,
+        uid,
+        model: JSON.stringify({
+          PubTitle: `申请人:${user?.userName || ''}`,
+          PubContent: `联系方式:${values['concat'] ?? ''}`,
+          PubRemind: values['remark'],
+          PubIsTrue: 1,
+        }),
+      };
+      break;
+    case 'pages/member/group_join':
+      if (values['mode'] === 'invite') {
+        payload = {
+          uid,
+          pubid: 9,
+          model: JSON.stringify({ PubTitle: values['mobile'], PubContent: values['tips'], CallName: values['call'] }),
+        };
+      } else {
+        const certificates = gallery(values['license']).map((item, index) => ({ ...item, name: `证书文件之${index + 1}` }));
+        payload = {
+          uid,
+          pubid: 8,
+          model: JSON.stringify({
+            PubTitle: values['name'],
+            PubContent: values['remark'],
+            BankAccount: values['bankacc'],
+            CertImgs: JSON.stringify(certificates),
+          }),
+        };
+      }
+      break;
+    case 'pages/member/myshop_add': {
+      const pictures = gallery(values['pics'] || String(loaded['pics'] ?? ''));
+      const model: Record<string, unknown> = { ...loaded, ...values, ID: id || loaded['ID'] || 0, Nodeid: values['Nodeid'] };
+      delete model['pics'];
+      payload = { uid, id, model: JSON.stringify(model), addon: JSON.stringify({ pics: JSON.stringify(pictures) }) };
+      break;
+    }
+    case 'pages/member/myshop_add_procon':
+      action = id ? 'product_upd' : 'product_add';
+      payload = { uid, id, model: JSON.stringify({ ID: id || 0, Procontent: values['Procontent'] }), addon: JSON.stringify({}) };
+      break;
+    case 'pages/member/feedback':
+      payload = {
+        model: JSON.stringify({ UserID: uid, Title: `${values['name']} 反馈的意见`, TContent: values['content'], Cateid: 22 }),
+      };
+      break;
+    case 'pages/member/passwd':
+    case 'pages/member/passwd2':
+      payload = {
+        uid,
+        vcode: values['vcode'],
+        newPass: values[meta.path.endsWith('passwd2') ? 'paypwd' : 'passwd'],
+        reNewPass: values[meta.path.endsWith('passwd2') ? 'paypwd2' : 'passwd2'],
+      };
+      break;
+    case 'pages/member/rnauth':
+      payload = {
+        uid,
+        realName: values['realName'],
+        idCard: values['idCard'],
+        idCardPics: JSON.stringify([image(values['front']), image(values['back']), image(values['handheld'])]),
+        mobile: values['mobile'] ?? user?.mobile ?? '',
+        address: values['address'] ?? '',
+      };
+      break;
+  }
+
+  return { action, payload };
+}
+
+function resolveSaveAction(action: string | undefined, hasId: boolean): string | undefined {
+  if (!action || !hasId) return action;
+  const updateMap: Record<string, string> = {
+    receaddr_add: 'receaddr_upd',
+    invoice_add: 'invoice_upd',
+    user_rnauth_add: 'user_rnauth_upd',
+    product_add: 'product_upd',
+  };
+  return updateMap[action] ?? action;
+}

+ 358 - 0
src/app/core/home-learning-modules.integration.spec.ts

@@ -0,0 +1,358 @@
+import { provideHttpClient } from '@angular/common/http';
+import { signal } from '@angular/core';
+import { TestBed } from '@angular/core/testing';
+import { provideRouter, Router } from '@angular/router';
+import { RouterTestingHarness } from '@angular/router/testing';
+import { Observable, of } from 'rxjs';
+import { routes } from '../app.routes';
+import { ApiService } from './api.service';
+import { ApiEnvelope, AppUser } from './models';
+import { PlatformService } from './platform.service';
+import { SessionService } from './session.service';
+
+class LearningModulesApiService {
+  lastScheduleParams: Record<string, unknown> = {};
+
+  get<T>(action: string, params: Record<string, unknown> = {}): Observable<ApiEnvelope<T>> {
+    if (action === 'content_list' && Number(params['nid']) === 291) {
+      return this.result<T>([
+        { GeneralID: 201, CreateTime: '2026-08-01 09:30:00', con: '初中英语@Unit 1', xxqs: JSON.stringify([{ Title: 'apple' }]) },
+        { GeneralID: 202, CreateTime: '2026-08-04 16:20:00', con: '高中英语@Unit 2' },
+      ]);
+    }
+    if (action === 'content_list' && Number(params['nid']) === 388) {
+      return this.result<T>([
+        { GeneralID: 100, Title: '张雅茜@历史任务', kywsj: '2026-05-15 09:00', fxzt: 1, yhid: 100, xxjlid: 900 },
+        { GeneralID: 101, Title: '张雅茜@任务', kywsj: '2026-08-01 09:00', fxzt: 1, yhid: 100, xxjlid: 900 },
+        { GeneralID: 102, Title: '张雅茜@任务', kywsj: '2026-08-02 09:00', fxzt: 0, yhid: 100, xxjlid: 900 },
+      ]);
+    }
+    if (action === 'content_list' && Number(params['nid']) === 3) {
+      return this.result<T>([{ GeneralID: 301, Title: '阅读理解专项' }]);
+    }
+    if (action === 'content_list' && Number(params['nid']) === 389) {
+      return this.result<T>([{ GeneralID: 401, df: 18, totalScore: 48, dontKnow: 20, wrong: 10 }]);
+    }
+    if (action === 'content_list' && Number(params['nid']) === 28) {
+      return this.result<T>([
+        { GeneralID: 801, kcid: 801, Title: '初中英语|七年级上册@同步词汇' },
+        { GeneralID: 802, kcid: 801, Title2: '【初中英语】阅读训练' },
+      ]);
+    }
+    if (action === 'content_list' && Number(params['nid']) === 29) {
+      this.lastScheduleParams = params;
+      return this.result<T>([
+        { GeneralID: 901, bxrq: '2026-08-10 00:00:00', sdsd: '19:00', Title: '同步伴学', plxm: '张老师', dszt: 10 },
+        { GeneralID: 902, bxrq: '2026-08-03 00:00:00', sdsd: '18:00', Title: '历史伴学', plxm: '李老师', dszt: 30 },
+      ]);
+    }
+    if (action === 'node_list' && Number(params['pid']) === 8) {
+      return this.result<T>([{ NodeID: 801, NodeName: '小学人教版词库' }]);
+    }
+    if (action === 'node_list' && Number(params['pid']) === 801) {
+      return this.result<T>([
+        { NodeID: 811, NodeName: 'Unit 1', yx_word: null, total: 20 },
+        { NodeID: 812, NodeName: 'Unit 2', yx_word: null, total: 18 },
+      ]);
+    }
+    if (action === 'e_ck_list') {
+      return this.result<T>([
+        { GeneralID: 1001, NodeID: 811, Title: 'apple', sy: 'n. 苹果', yp: 'apple.mp3', w_learned: 1 },
+        { GeneralID: 1002, NodeID: 812, Title: 'banana', sy: 'n. 香蕉', yp: 'banana.mp3', w_learned: 0 },
+      ]);
+    }
+    if (action === 'node_get') return this.result<T>([{ NodeID: 3, NodeName: '专项训练' }]);
+    if (action === 'vote_ask') return this.result<T>([{ ID: 1, Title: '第一组问题' }]);
+    if (action === 'vote_question') {
+      return this.result<T>([{ ID: 501, AskID: 1, QType: 1, QTitle: 'apple', QContent: '苹果', QOption: JSON.stringify([{ text: '苹果', value: 1 }]) }]);
+    }
+    if (action === 'e_words_list') {
+      return this.result<T>([{
+        GeneralID: 601,
+        Title: 'tend@张雅茜',
+        detail: [{ GeneralID: 701, Title: 'tend', sy: 'vt.照顾;照料', yp: 'tend.mp3' }],
+      }]);
+    }
+    return this.result<T>([]);
+  }
+
+  post<T>(action: string): Observable<ApiEnvelope<T>> {
+    if (action === 'e_get_21list') {
+      return this.result<T>([
+        { GeneralID: 101, Title: '张雅茜@任务', kc_title: '初中英语复习', kywsj: '2020-08-01 00:00:00', fxzt: 1, yhid: 100 },
+        { GeneralID: 102, Title: '张雅茜@任务', kc_title: '高中英语复习', kywsj: '2020-08-02 00:00:00', fxzt: 0, yhid: 100 },
+      ]);
+    }
+    if (action === 'e_record_detail') {
+      return of({
+        retcode: 1,
+        result: [{ GeneralID: 701, Title: 'apple', detail: [{ GeneralID: 701, Title: 'apple', sy: '苹果', yp: '' }] }],
+        addon: [{ if_writing: '0', if_writing_tips: '先回忆单词,再查看释义' }],
+      } as unknown as ApiEnvelope<T>);
+    }
+    return this.result<T>([]);
+  }
+
+  assetUrl(path: unknown): string { return String(path ?? ''); }
+
+  private result<T>(result: unknown): Observable<ApiEnvelope<T>> {
+    return of({ retcode: 1, result: result as T });
+  }
+}
+
+describe('home learning modules', () => {
+  const user = signal<AppUser | null>({ userId: 100, userName: 'student', honeyName: '张雅茜', groupId: 1 });
+  const session = {
+    user,
+    token: signal('test-token'),
+    student: signal<AppUser | null>(null),
+    isLoggedIn: signal(true),
+    groupId: signal(1),
+    setStudent: () => undefined,
+    patchUser: () => undefined,
+    logout: () => undefined,
+  };
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [
+        provideRouter(routes),
+        provideHttpClient(),
+        { provide: ApiService, useClass: LearningModulesApiService },
+        { provide: SessionService, useValue: session },
+      ],
+    });
+  });
+
+  it('renders source-shaped data for all four home entries', async () => {
+    const harness = await RouterTestingHarness.create();
+
+    await harness.navigateByUrl('/pages/home/index');
+    expect(harness.fixture.nativeElement.querySelector('.tabbar')).not.toBeNull();
+    expect(harness.routeNativeElement?.querySelectorAll('.home-header .header-action').length).toBe(1);
+    expect(harness.routeNativeElement?.querySelector('.home-header .header-action')?.getAttribute('href')).toBe('/pages/home/search');
+    expect(harness.routeNativeElement?.querySelector('.home-header [aria-label="用户中心"]')).toBeNull();
+    expect(harness.routeNativeElement?.querySelector('.memory-hero')).not.toBeNull();
+    expect(harness.routeNativeElement?.querySelectorAll('.memory-dot').length).toBe(21);
+    expect(harness.routeNativeElement?.querySelector('.today-panel')?.textContent).toContain('学习新词');
+    expect(harness.routeNativeElement?.querySelectorAll('.student-stats > div').length).toBe(5);
+    expect(harness.routeNativeElement?.querySelectorAll('.quick-grid > a').length).toBe(4);
+    expect(harness.routeNativeElement?.querySelector('.continue-button')?.getAttribute('href')).toBe('/pages/stu/stu_calendar');
+    expect(harness.routeNativeElement?.querySelector('.task-link')?.getAttribute('href')).toBe('/pages/home/cart');
+
+    await harness.navigateByUrl('/pages/stu/index');
+    expect(harness.routeNativeElement?.querySelectorAll('.catalog-tabs button').length).toBe(5);
+    expect(harness.routeNativeElement?.textContent).toContain('小学人教版词库');
+    (harness.routeNativeElement?.querySelector('.node-list button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    expect(TestBed.inject(Router).url).toBe('/pages/stu/stu_list?nid=801');
+    expect(harness.routeNativeElement?.querySelectorAll('.node-list button').length).toBe(2);
+    expect(harness.routeNativeElement?.querySelector('.node-list button')?.textContent).toContain('已学 1/20 · 进度 5%');
+    expect(harness.routeNativeElement?.querySelectorAll('.unit-progress-ring').length).toBe(2);
+    expect(harness.routeNativeElement?.querySelector('.unit-progress-ring')?.textContent).toContain('5%');
+    const units = harness.routeNativeElement?.querySelectorAll('.node-list button') as NodeListOf<HTMLButtonElement>;
+    units[0].click();
+    units[1].click();
+    harness.fixture.detectChanges();
+    expect(harness.routeNativeElement?.querySelector('.unit-selection-summary')?.textContent).toContain('已选 2 个单元');
+    (harness.routeNativeElement?.querySelector('.unit-actions .primary-button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    const selectionRouter = TestBed.inject(Router);
+    expect(selectionRouter.url).toContain('/pages/stu/stu_abc');
+    expect(selectionRouter.parseUrl(selectionRouter.url).queryParams['nids']).toBe('811,812');
+    expect(harness.routeNativeElement?.querySelector('.selection-header')?.textContent).toContain('0 / 30');
+    expect(harness.routeNativeElement?.querySelectorAll('.word-refine-item').length).toBe(2);
+    expect(harness.routeNativeElement?.querySelector('.word-refine-item')?.textContent).not.toContain('n. 苹果');
+    const playSpy = spyOn(TestBed.inject(PlatformService), 'play');
+    const firstRefineWord = harness.routeNativeElement?.querySelector('.word-refine-item') as HTMLElement;
+    (firstRefineWord.querySelector('.word-reveal') as HTMLButtonElement).click();
+    harness.fixture.detectChanges();
+    expect(firstRefineWord.textContent).toContain('n. 苹果');
+    expect(playSpy).toHaveBeenCalledWith('UploadFiles/apple.mp3');
+    (firstRefineWord.querySelector('.unknown-button') as HTMLButtonElement).click();
+    harness.fixture.detectChanges();
+    expect(firstRefineWord.classList).toContain('selected');
+    expect(harness.routeNativeElement?.querySelector('.selection-header')?.textContent).toContain('已选 1 / 30');
+    (firstRefineWord.querySelector('.known-button') as HTMLButtonElement).click();
+    harness.fixture.detectChanges();
+    expect(firstRefineWord.classList).not.toContain('selected');
+    expect(harness.routeNativeElement?.querySelector('.selection-header')?.textContent).toContain('已选 0 / 30');
+    (firstRefineWord.querySelector('.unknown-button') as HTMLButtonElement).click();
+    harness.fixture.detectChanges();
+    (harness.routeNativeElement?.querySelector('.selection-header .primary-button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    expect(selectionRouter.url).toContain('/pages/stu/stu_con');
+    expect(harness.routeNativeElement?.querySelectorAll('.repeat-list > button').length).toBe(1);
+
+    await harness.navigateByUrl('/pages/stu/stu_calendar');
+    expect(harness.fixture.nativeElement.querySelector('.tabbar')).toBeNull();
+    expect(harness.routeNativeElement?.querySelectorAll('.memory-date').length).toBe(42);
+    expect(harness.routeNativeElement?.querySelectorAll('.memory-date.completed, .memory-date.due').length).toBe(2);
+    expect(harness.routeNativeElement?.querySelector('.memory-overview')?.textContent).toContain('2 / 3');
+    expect(harness.routeNativeElement?.querySelector('.memory-date-tasks')?.textContent).toContain('高中英语复习');
+    expect(harness.routeNativeElement?.querySelector('.review-sequence')?.textContent).toContain('第 3 次复习');
+    for (let index = 0; index < 3; index += 1) {
+      (harness.routeNativeElement?.querySelector('[aria-label="上个月"]') as HTMLButtonElement).click();
+      harness.fixture.detectChanges();
+    }
+    expect(harness.routeNativeElement?.querySelector('[aria-label^="2026-05-15"]')?.classList).toContain('completed');
+    expect(harness.routeNativeElement?.textContent).toContain('张雅茜');
+    expect(harness.routeNativeElement?.querySelector('.memory-overview header b')?.textContent).toContain('67%');
+    expect(harness.routeNativeElement?.querySelector('.memory-stats')?.textContent).toContain('2 / 3');
+
+    await harness.navigateByUrl('/pages/home/ucenter');
+    expect(harness.fixture.nativeElement.querySelector('.tabbar')).not.toBeNull();
+    expect(harness.routeNativeElement?.querySelector('.new-word-entry')?.getAttribute('href')).toBe('/pages/home/cart');
+    expect(harness.routeNativeElement?.querySelector('.course-memory')?.textContent).toContain('21 天抗遗忘计划');
+    expect(harness.routeNativeElement?.querySelector('.memory-overview em')?.textContent).toContain('67%');
+    expect(harness.routeNativeElement?.querySelector('.memory-overview')?.textContent).toContain('覆盖 3 个复习日');
+    expect(harness.routeNativeElement?.querySelector('.memory-counts')?.textContent).toContain('3总任务');
+    expect(harness.routeNativeElement?.querySelector('.memory-counts')?.textContent).toContain('2已完成');
+    expect(harness.routeNativeElement?.querySelector('.memory-counts')?.textContent).toContain('1到期待复习');
+    expect(harness.routeNativeElement?.querySelector('.memory-counts')?.textContent).toContain('0未来待复习');
+
+    await harness.navigateByUrl('/pages/stu/record?id=827482&gid=828268&kcid=203');
+    const recordWord = harness.routeNativeElement?.querySelector('.record-word') as HTMLButtonElement;
+    expect(recordWord.querySelector('.record-copy strong')?.textContent).toBe('apple');
+    expect(harness.routeNativeElement?.textContent).not.toContain('苹果');
+    recordWord.click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    expect(recordWord.querySelector('.record-copy strong')?.textContent).toBe('苹果');
+    expect(harness.routeNativeElement?.querySelector('.record-overview')?.textContent).toContain('1 / 1');
+
+    await harness.navigateByUrl('/pages/member/starshop');
+    expect(harness.fixture.nativeElement.querySelector('.tabbar')).toBeNull();
+    expect(harness.routeNativeElement?.querySelector('.word-select-list strong')?.textContent).toBe('tend');
+    expect(harness.routeNativeElement?.querySelector('.word-select-list')?.textContent).not.toContain('@张雅茜');
+    (harness.routeNativeElement?.querySelector('.word-select-list > button') as HTMLButtonElement).click();
+    (harness.routeNativeElement?.querySelector('.selection-header .primary-button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    const selectedNewWords = JSON.parse(sessionStorage.getItem('xiaoshu.selectedWords') ?? '[]') as Array<{ word?: string }>;
+    expect(selectedNewWords[0]?.word).toBe('tend');
+    expect(sessionStorage.getItem('xiaoshu.selectedWords')).not.toContain('@张雅茜');
+
+    await harness.navigateByUrl('/pages/his/well?nid=3');
+    expect(harness.fixture.nativeElement.querySelector('.tabbar')).toBeNull();
+    expect(harness.routeNativeElement?.textContent).toContain('阅读理解专项');
+
+    await harness.navigateByUrl('/pages/home/cart');
+    expect(harness.fixture.nativeElement.querySelector('.tabbar')).not.toBeNull();
+    expect(harness.fixture.nativeElement.querySelector('.app-header .icon-button')).toBeNull();
+    expect(harness.fixture.nativeElement.querySelector('.app-header h1')?.textContent).toContain('伴学中心');
+    const libraryEntry = harness.routeNativeElement?.querySelector('.library-entry') as HTMLButtonElement;
+    expect(libraryEntry.textContent).toContain('选择词库');
+    expect(libraryEntry.textContent).toContain('按学段选择教材与单元');
+    expect(harness.routeNativeElement?.querySelectorAll('.course-list li').length).toBe(2);
+    expect(harness.routeNativeElement?.querySelectorAll('.course-list button').length).toBe(2);
+    expect((harness.routeNativeElement?.querySelector('.course-list button') as HTMLButtonElement).disabled).toBeFalse();
+    expect(harness.routeNativeElement?.textContent).toContain('【七年级上册】同步词汇');
+    expect(harness.routeNativeElement?.textContent).toContain('进入课程,选择本次学习单元');
+    (harness.routeNativeElement?.querySelector('.course-list button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    expect(TestBed.inject(Router).url).toBe('/pages/stu/stu_list?nid=801');
+    expect(harness.routeNativeElement?.querySelectorAll('.node-list button').length).toBe(2);
+
+    await harness.navigateByUrl('/pages/home/cart');
+    (harness.routeNativeElement?.querySelector('.library-entry') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    expect(TestBed.inject(Router).url).toBe('/pages/stu/index');
+    expect(harness.routeNativeElement?.textContent).toContain('选择词库教材');
+
+    await harness.navigateByUrl('/pages/home/cart');
+    expect(harness.routeNativeElement?.querySelectorAll('.schedule-list article').length).toBe(2);
+    expect(harness.routeNativeElement?.textContent).toContain('同步伴学');
+    expect(harness.routeNativeElement?.textContent).toContain('历史伴学');
+    const api = TestBed.inject(ApiService) as unknown as LearningModulesApiService;
+    expect(api.lastScheduleParams['dateFilter']).toBeUndefined();
+    expect(api.lastScheduleParams['orders']).toBe('B.yysj=DESC');
+    const finishedFilter = Array.from(harness.routeNativeElement?.querySelectorAll('.schedule-filters button') ?? [])
+      .find((button) => button.textContent?.includes('已结束')) as HTMLButtonElement;
+    finishedFilter.click();
+    harness.fixture.detectChanges();
+    expect(harness.routeNativeElement?.querySelectorAll('.schedule-list article').length).toBe(1);
+    expect(harness.routeNativeElement?.textContent).toContain('历史伴学');
+    const allFilter = harness.routeNativeElement?.querySelector('.schedule-filters button') as HTMLButtonElement;
+    allFilter.click();
+    harness.fixture.detectChanges();
+    (harness.routeNativeElement?.querySelector('.schedule-list article') as HTMLElement).click();
+    await harness.fixture.whenStable();
+    expect(TestBed.inject(Router).url).toBe('/pages/shop/stu_get_order?id=901');
+
+    await harness.navigateByUrl('/pages/his/his');
+    expect(harness.fixture.nativeElement.querySelector('.tabbar')).toBeNull();
+    const aiReadingFeature = harness.routeNativeElement?.querySelector('.ai-reading-feature') as HTMLElement;
+    expect(aiReadingFeature.textContent).toContain('AI 阅读巩固');
+    expect(aiReadingFeature.textContent).toContain('生成阅读');
+    expect(aiReadingFeature.textContent).toContain('AI 批改');
+    expect(aiReadingFeature.querySelector('img')?.getAttribute('src')).toBe('/static/images/ai-reading-journey.png');
+    const aiReadingCta = aiReadingFeature.querySelector('.ai-reading-cta') as HTMLButtonElement;
+    expect(aiReadingCta.disabled).toBeFalse();
+    expect(aiReadingFeature.textContent).toContain('最近学过的 1 个单词');
+    expect(aiReadingCta.textContent).toContain('立即生成阅读');
+    aiReadingCta.click();
+    await harness.fixture.whenStable();
+    const aiReadingRouter = TestBed.inject(Router);
+    expect(aiReadingRouter.url).toContain('/pages/iframe/index');
+    expect(aiReadingRouter.parseUrl(aiReadingRouter.url).queryParams['returnUrl']).toBe('/pages/his/his');
+    expect(JSON.parse(aiReadingRouter.parseUrl(aiReadingRouter.url).queryParams['params'])['GeneralID']).toBe(201);
+
+    await harness.navigateByUrl('/pages/his/his');
+    const currentLearningCard = harness.routeNativeElement?.querySelector('.current-learning-card') as HTMLElement;
+    const learningRecordCard = harness.routeNativeElement?.querySelector('.learning-record-card') as HTMLElement;
+    expect(currentLearningCard.textContent).toContain('继续选词学习');
+    expect(currentLearningCard.querySelectorAll('.current-learning-list button').length).toBe(2);
+    expect(currentLearningCard.compareDocumentPosition(learningRecordCard) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
+    (currentLearningCard.querySelector('.current-learning-list button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    harness.fixture.detectChanges();
+    expect(TestBed.inject(Router).url).toBe('/pages/stu/stu_list?nid=801');
+    expect(harness.routeNativeElement?.querySelectorAll('.node-list button').length).toBe(2);
+
+    await harness.navigateByUrl('/pages/his/his');
+    expect(harness.routeNativeElement?.textContent).toContain('初中英语 / Unit 1');
+    expect(harness.routeNativeElement?.querySelectorAll('.calendar-day').length).toBe(42);
+    expect(harness.routeNativeElement?.querySelector('.calendar-day.learned')?.getAttribute('aria-label')).toContain('2026-08-01');
+    expect(harness.routeNativeElement?.querySelector('.learning-calendar footer')?.textContent).toContain('本月学习 2 天,共 2 次');
+    expect(harness.routeNativeElement?.querySelectorAll('.learning-record-card .learning-item').length).toBe(2);
+    (harness.routeNativeElement?.querySelector('[aria-label^="2026-08-01"]') as HTMLButtonElement).click();
+    harness.fixture.detectChanges();
+    expect(harness.routeNativeElement?.querySelectorAll('.learning-record-card .learning-item').length).toBe(1);
+    expect(harness.routeNativeElement?.querySelector('.learning-record-card')?.textContent).not.toContain('高中英语 / Unit 2');
+    (harness.routeNativeElement?.querySelector('.record-actions button') as HTMLButtonElement).click();
+    harness.fixture.detectChanges();
+    expect(harness.routeNativeElement?.querySelectorAll('.learning-record-card .learning-item').length).toBe(2);
+    (harness.routeNativeElement?.querySelector('.reading-button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    const router = TestBed.inject(Router);
+    expect(router.url).toContain('/pages/iframe/index');
+    expect(router.parseUrl(router.url).queryParams['returnUrl']).toBe('/pages/his/his');
+    expect(harness.routeNativeElement?.querySelector('.back-button')).not.toBeNull();
+    (harness.routeNativeElement?.querySelector('.back-button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    expect(router.url).toBe('/pages/his/his');
+    (harness.fixture.nativeElement.querySelector('.app-header .icon-button') as HTMLButtonElement).click();
+    await harness.fixture.whenStable();
+    expect(router.url).toBe('/pages/home/index');
+
+    await harness.navigateByUrl('/pages/his/exam');
+    expect(harness.routeNativeElement?.textContent).toContain('第一组问题');
+    expect(harness.routeNativeElement?.textContent).toContain('相当于初二水平');
+    expect(harness.routeNativeElement?.textContent).toContain('共 1 题');
+
+    const startButton = harness.routeNativeElement?.querySelector('.exam-intro > button') as HTMLButtonElement;
+    startButton.click();
+    harness.fixture.detectChanges();
+    expect(harness.routeNativeElement?.querySelector('.question-word')?.textContent).toContain('apple');
+    expect(harness.routeNativeElement?.querySelector('.question-card > h2')).toBeNull();
+    expect(harness.routeNativeElement?.querySelectorAll('.exam-options button').length).toBe(1);
+
+    (harness.routeNativeElement?.querySelector('.exam-options button') as HTMLButtonElement).click();
+    harness.fixture.detectChanges();
+  });
+});

+ 113 - 0
src/app/core/models.ts

@@ -0,0 +1,113 @@
+export type ApiMethod = 'get' | 'post';
+export type PageKind =
+  | 'home'
+  | 'account'
+  | 'article'
+  | 'login'
+  | 'resource'
+  | 'form'
+  | 'learning'
+  | 'game'
+  | 'product'
+  | 'productList'
+  | 'checkout'
+  | 'order'
+  | 'finance'
+  | 'appointment'
+  | 'courseCenter'
+  | 'studyRecords'
+  | 'team'
+  | 'merchant'
+  | 'webview'
+  | 'support'
+  | 'supportFeedback';
+
+export interface ApiEnvelope<T = unknown> {
+  retcode: number;
+  retmsg?: string;
+  result: T;
+  addon?: unknown;
+  page?: {
+    itemCount?: number;
+    pageCount?: number;
+    pageIndex?: number;
+    pageSize?: number;
+    [key: string]: unknown;
+  };
+}
+
+export interface AppUser {
+  userId: number;
+  userName?: string;
+  honeyName?: string;
+  userFace?: string;
+  mobile?: string;
+  groupId?: number;
+  groupName?: string;
+  purse?: number;
+  point?: number;
+  silverCoin?: number;
+  [key: string]: unknown;
+}
+
+export interface ApiBinding {
+  action: string;
+  method?: ApiMethod;
+  params?: Record<string, string | number | boolean>;
+  resultPath?: string;
+  queryMap?: Record<string, string>;
+}
+
+export interface FormField {
+  key: string;
+  label: string;
+  type?: 'text' | 'password' | 'tel' | 'number' | 'email' | 'textarea' | 'select' | 'date' | 'file' | 'checkbox';
+  required?: boolean;
+  placeholder?: string;
+  options?: Array<{ label: string; value: string | number }>;
+}
+
+export interface PageAction {
+  key?: string;
+  label: string;
+  action: string;
+  method?: ApiMethod;
+  tone?: 'primary' | 'danger' | 'quiet';
+  confirm?: string;
+  fields?: FormField[];
+  scope?: 'page' | 'item';
+  params?: Record<string, string | number | boolean>;
+  when?: Record<string, string | number | boolean>;
+  success?: 'reload' | 'remove-item' | 'none';
+}
+
+export interface PageMeta {
+  path: string;
+  title: string;
+  domain: 'home' | 'shop' | 'learn' | 'history' | 'member' | 'support' | 'game' | 'system';
+  kind: PageKind;
+  auth?: boolean;
+  hideHeader?: boolean;
+  hideBack?: boolean;
+  hideTabbar?: boolean;
+  api?: ApiBinding;
+  fields?: FormField[];
+  saveAction?: string;
+  deleteAction?: string;
+  itemRoute?: string;
+  queryKey?: string;
+  emptyText?: string;
+  description?: string;
+  actions?: PageAction[];
+  tabs?: string[];
+  tabActions?: string[];
+  tabParams?: Array<Record<string, string | number | boolean>>;
+  onLoadActions?: ApiBinding[];
+}
+
+export interface NavItem {
+  path: string;
+  label: string;
+  icon: string;
+  roles?: number[];
+}

+ 26 - 0
src/app/core/page-catalog.spec.ts

@@ -0,0 +1,26 @@
+import { routes } from '../app.routes';
+import { PAGE_CATALOG } from './page-catalog';
+
+describe('migration route catalog', () => {
+  it('contains all 95 legacy routes exactly once', () => {
+    expect(PAGE_CATALOG.length).toBe(95);
+    expect(new Set(PAGE_CATALOG.map((page) => page.path)).size).toBe(95);
+  });
+
+  it('registers every catalog entry in Angular Router', () => {
+    const children = routes[0].children ?? [];
+    const routePaths = new Set(children.map((route) => route.path));
+    for (const page of PAGE_CATALOG) expect(routePaths.has(page.path)).withContext(page.path).toBeTrue();
+  });
+
+  it('keeps usable metadata on every route', () => {
+    for (const page of PAGE_CATALOG) {
+      expect(page.path).withContext('path').toMatch(/^(pages|components)\//);
+      expect(page.title.length).withContext(page.path).toBeGreaterThan(0);
+      if (page.kind === 'form') {
+        expect(page.fields?.length).withContext(`${page.path} fields`).toBeGreaterThan(0);
+        expect(page.saveAction).withContext(`${page.path} save action`).toBeTruthy();
+      }
+    }
+  });
+});

+ 116 - 0
src/app/core/page-catalog.ts

@@ -0,0 +1,116 @@
+import { FormField, PageMeta } from './models';
+
+const text = (key: string, label: string, required = false, type: FormField['type'] = 'text'): FormField => ({
+  key,
+  label,
+  required,
+  type,
+  placeholder: `请输入${label}`,
+});
+
+const mobile = text('mobile', '手机号码', true, 'tel');
+const title = text('title', '标题', true);
+const content = text('content', '内容', true, 'textarea');
+
+export const PAGE_CATALOG: PageMeta[] = [
+  { path: 'pages/home/index', title: '小树陪练', domain: 'home', kind: 'home', auth: true, hideHeader: true },
+  { path: 'pages/home/ucenter', title: '用户中心', domain: 'home', kind: 'account', hideHeader: true },
+  { path: 'pages/home/shop', title: '陪练内容', domain: 'shop', kind: 'courseCenter', auth: true, hideHeader: true },
+  { path: 'pages/home/cart', title: '伴学中心', domain: 'learn', kind: 'courseCenter', auth: true, hideBack: true },
+  { path: 'pages/home/search', title: '搜索', domain: 'shop', kind: 'productList' },
+  { path: 'pages/home/enjoy', title: '文章分类', domain: 'home', kind: 'resource', api: { action: 'content_list' }, onLoadActions: [{ action: 'node_get' }], itemRoute: '/pages/home/enjoy_item', emptyText: '暂无文章' },
+  { path: 'pages/home/enjoy_item', title: '内容详情', domain: 'home', kind: 'article', api: { action: 'content_get' } },
+  { path: 'pages/home/help', title: '帮助', domain: 'system', kind: 'resource', description: '使用帮助与常见问题' },
+  { path: 'pages/home/webview', title: '主页', domain: 'system', kind: 'webview' },
+  { path: 'components/bytedesk_kefu/chat-kf', title: '萝卜丝智能客服', domain: 'support', kind: 'support', hideTabbar: true },
+  { path: 'components/bytedesk_kefu/chat-im', title: '在线客服', domain: 'support', kind: 'support', hideTabbar: true },
+  { path: 'components/bytedesk_kefu/rate', title: '满意度评价', domain: 'support', kind: 'supportFeedback', hideTabbar: true },
+  { path: 'components/bytedesk_kefu/webview', title: '客服网页', domain: 'support', kind: 'webview', hideTabbar: true },
+  { path: 'components/bytedesk_kefu/leavemsg', title: '留言', domain: 'support', kind: 'supportFeedback', hideTabbar: true },
+
+  { path: 'pages/stu/index', title: '词库中心', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu_list', title: '单元列表', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu_con', title: '英语词汇列表', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/game/game', title: '吊小人猜单词', domain: 'game', kind: 'game', hideTabbar: true },
+  { path: 'pages/game/index', title: '游戏中心', domain: 'game', kind: 'game' },
+  { path: 'pages/stu/stu_abc', title: '词库精筛', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu3', title: '单词对对碰', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu4', title: '听音识意', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu5', title: '组合拼写', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu6', title: '学后检测', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu_ok', title: '完成报表', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu_calendar', title: '科学抗遗忘系统', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/stu7', title: '科学抗遗忘任务', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/home/app_update', title: '版本更新', domain: 'system', kind: 'resource', api: { action: 'app_update' }, description: 'Web 版本会随页面刷新自动更新' },
+  { path: 'pages/his/his', title: '学习记录', domain: 'history', kind: 'studyRecords', auth: true },
+  { path: 'pages/game/wam', title: '打地鼠', domain: 'game', kind: 'game', hideTabbar: true },
+  { path: 'pages/game/plane', title: '飞机大战', domain: 'game', kind: 'game', hideTabbar: true },
+  { path: 'pages/game/downletter', title: '字母雨', domain: 'game', kind: 'game', hideTabbar: true },
+  { path: 'pages/his/his_words', title: '选词学习', domain: 'history', kind: 'learning', auth: true },
+  { path: 'pages/his/exam', title: '词汇测试', domain: 'history', kind: 'learning', auth: true },
+  { path: 'pages/iframe/index', title: 'AI 阅读设置', domain: 'learn', kind: 'webview', hideHeader: true, hideTabbar: true },
+  { path: 'pages/game/llk', title: '单词连连看', domain: 'game', kind: 'game', auth: true, hideTabbar: true },
+  { path: 'pages/his/well', title: '专项训练', domain: 'history', kind: 'resource', auth: true, api: { action: 'content_list', params: { nid: 3 } }, onLoadActions: [{ action: 'node_get', params: { id: 3 } }], itemRoute: '/pages/home/enjoy_item' },
+  { path: 'pages/help/help', title: '帮助中心', domain: 'system', kind: 'resource', api: { action: 'content_list', params: { nid: 363 } }, onLoadActions: [{ action: 'node_get' }], itemRoute: '/pages/home/enjoy_item' },
+  { path: 'pages/stu/pl_stu5', title: '陪练拼写', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/pl_end', title: '学习结束', domain: 'learn', kind: 'learning', auth: true },
+  { path: 'pages/stu/record', title: '复习', domain: 'learn', kind: 'learning', auth: true, hideTabbar: true },
+  { path: 'pages/his/his_stu', title: '我的学员', domain: 'history', kind: 'resource', auth: true, api: { action: 'e_user_list', method: 'post' }, itemRoute: '/pages/his/his' },
+
+  { path: 'pages/shop/shop_item', title: '商品详情', domain: 'shop', kind: 'product', api: { action: 'product_get' }, hideTabbar: true },
+  { path: 'pages/shop/shop_item_comment', title: '评论列表', domain: 'shop', kind: 'resource', api: { action: 'order_comment_list' }, emptyText: '暂无评论' },
+  { path: 'pages/shop/shop_class', title: '商品分类', domain: 'shop', kind: 'productList' },
+  { path: 'pages/shop/shop_nodes', title: '商品类别', domain: 'shop', kind: 'productList' },
+  { path: 'pages/shop/get_order', title: '预约详情', domain: 'shop', kind: 'appointment', auth: true },
+  { path: 'pages/shop/get_order_vip', title: '订单结算', domain: 'shop', kind: 'checkout', auth: true, hideTabbar: true },
+  { path: 'pages/shop/order', title: '订单详情', domain: 'shop', kind: 'order', auth: true },
+  { path: 'pages/shop/order_appeal', title: '售后', domain: 'shop', kind: 'form', auth: true, api: { action: 'order_get' }, saveAction: 'pub_add', fields: [{ key: 'type', label: '申请类型', type: 'select', required: true, options: [{ label: '退货', value: 10 }, { label: '换货', value: 11 }] }, text('concat', '联系手机', true, 'tel'), text('reason', '申请理由', true, 'textarea'), text('images', '凭证图片', false, 'file')] },
+  { path: 'pages/shop/order_comment', title: '订单评价', domain: 'shop', kind: 'resource', auth: true, api: { action: 'order_comment_list' }, itemRoute: '/pages/shop/order_comment_add' },
+  { path: 'pages/shop/order_comment_add', title: '添加评价', domain: 'shop', kind: 'form', auth: true, api: { action: 'order_get' }, saveAction: 'order_comment_add', fields: [{ key: 'proId', label: '评价商品', type: 'select', required: true, options: [] }, { key: 'anonymous', label: '匿名评价', type: 'checkbox', placeholder: '隐藏昵称及头像' }, title, text('score', '评分', true, 'number'), content, text('images', '评价图片', false, 'file')] },
+  { path: 'pages/shop/coupon', title: '商城优惠券', domain: 'shop', kind: 'resource', auth: true, api: { action: 'coupon_list' }, actions: [{ key: 'receive', label: '领取', action: 'coupon_usrgot_add', method: 'post', scope: 'item', success: 'reload' }] },
+  { path: 'pages/shop/stu_get_order', title: '预约详情', domain: 'shop', kind: 'appointment', auth: true },
+  { path: 'pages/shop/baobiao', title: '学习报表', domain: 'shop', kind: 'webview', auth: true },
+
+  { path: 'pages/member/point', title: '我的学习令', domain: 'member', kind: 'finance', auth: true },
+  { path: 'pages/member/purse', title: '我的钱包', domain: 'member', kind: 'finance', auth: true },
+  { path: 'pages/member/purse_outacc', title: '提现账户管理', domain: 'member', kind: 'resource', auth: true, api: { action: 'user_bank_list' }, itemRoute: '/pages/member/purse_outacc_add', deleteAction: 'user_bank_del' },
+  { path: 'pages/member/purse_outacc_add', title: '提现账户详情', domain: 'member', kind: 'form', auth: true, api: { action: 'user_bank_get' }, saveAction: 'user_bank_add', fields: [{ key: 'CardType', label: '账户类型', type: 'select', required: true, options: ['银行卡', '支付宝', '微信', '其它'].map((value) => ({ label: value, value })) }, text('BankName', '银行名称', true), text('PeopleName', '开户人', true), text('CardNum', '开户账号', true), text('Remark', '备注', false, 'textarea')] },
+  { path: 'pages/member/purse_out_log', title: '提现记录', domain: 'member', kind: 'resource', auth: true, api: { action: 'user_cash_list' } },
+  { path: 'pages/member/settings', title: '个人设置', domain: 'member', kind: 'form', auth: true, api: { action: 'user_get' }, saveAction: 'user_update', fields: [text('honeyName', '昵称', true), mobile, text('userFace', '头像', false, 'file'), { key: 'sex', label: '性别', type: 'select', options: [{ label: '男', value: '男' }, { label: '女', value: '女' }] }, text('birthday', '生日', false, 'date')] },
+  { path: 'pages/member/wxauth', title: '登录系统', domain: 'member', kind: 'login', hideTabbar: true },
+  { path: 'pages/member/wxauth_after', title: '完善个人信息', domain: 'member', kind: 'form', auth: true, hideHeader: true, hideTabbar: true, saveAction: 'user_update_pwdall', fields: [text('passwd', '登录密码', true, 'password'), text('passwd2', '确认登录密码', true, 'password'), text('paypwd', '支付密码', true, 'password'), text('paypwd2', '确认支付密码', true, 'password')] },
+  { path: 'pages/member/receaddr', title: '收货地址', domain: 'member', kind: 'resource', auth: true, api: { action: 'receaddr_list' }, itemRoute: '/pages/member/receaddr_add', deleteAction: 'receaddr_del' },
+  { path: 'pages/member/receaddr_add', title: '编辑收货地址', domain: 'member', kind: 'form', auth: true, api: { action: 'receaddr_get' }, saveAction: 'receaddr_add', fields: [text('ReceivName', '收货人', true), text('MobileNum', '手机号码', true, 'tel'), text('Provinces', '所在地区', true), text('Street', '详细地址', true, 'textarea'), text('Zipcode', '邮政编码')] },
+  { path: 'pages/member/invoice', title: '我的发票', domain: 'member', kind: 'resource', auth: true, api: { action: 'invoice_list' }, itemRoute: '/pages/member/invoice_add', deleteAction: 'invoice_del' },
+  { path: 'pages/member/invoice_add', title: '编辑发票', domain: 'member', kind: 'form', auth: true, api: { action: 'invoice_get' }, saveAction: 'invoice_add', fields: [{ key: 'InvType', label: '发票类型', type: 'select', required: true, options: [{ label: '个人', value: 0 }, { label: '企业', value: 1 }] }, text('InvHead', '发票抬头', true), text('UserCode', '身份证或信用代码', true), text('UserEmail', '联系邮箱', true, 'email'), text('UserMobile', '注册电话', false, 'tel'), text('UserAddress', '注册地址'), text('UserBankName', '开户银行'), text('UserBankAccount', '银行账号')] },
+  { path: 'pages/member/myorder', title: '成长中心', domain: 'member', kind: 'studyRecords', auth: true },
+  { path: 'pages/member/myorder_appeal', title: '退换货申请', domain: 'member', kind: 'studyRecords', auth: true },
+  { path: 'pages/member/spread', title: '我的团队', domain: 'member', kind: 'team', auth: true },
+  { path: 'pages/member/vip', title: '学习合伙人', domain: 'member', kind: 'team', auth: true },
+  { path: 'pages/member/group', title: '学习代理商', domain: 'member', kind: 'team', auth: true },
+  { path: 'pages/member/group_detail', title: '我的团队', domain: 'member', kind: 'team', auth: true },
+  { path: 'pages/member/acc_destory', title: '隐私注销通道', domain: 'member', kind: 'form', auth: true, api: { action: 'pub_list', params: { pubid: 7 } }, saveAction: 'pub_add', fields: [text('concat', '联系方式', true, 'tel'), text('remark', '注销原因', true, 'textarea')] },
+  { path: 'pages/member/mycoupon', title: '我的优惠券', domain: 'member', kind: 'resource', auth: true, api: { action: 'coupon_usrgot_list' }, tabs: ['未使用', '已使用', '已过期'], tabActions: ['coupon_usrgot_list', 'coupon_usrgot_list', 'coupon_usrgot_list'], tabParams: [{ fast: 'idle' }, { fast: 'used' }, { fast: 'expired' }] },
+  { path: 'pages/member/group_join', title: '成为供应商', domain: 'member', kind: 'form', auth: true, api: { action: 'user_get' }, saveAction: 'pub_add', description: '初级合伙人及以上可申请成为供应商,也可邀请符合资质要求的商户入驻。', fields: [{ key: 'mode', label: '办理方式', type: 'select', required: true, options: [{ label: '申请入驻', value: 'supplier' }, { label: '邀请商户', value: 'invite' }] }, text('name', '供应商名称'), text('bankacc', '对公银行账号'), text('license', '资质证书', false, 'file'), text('remark', '申请备注', false, 'textarea'), text('mobile', '对方联系手机号', false, 'tel'), text('call', '联系时称呼'), text('tips', '交流注意点', false, 'textarea')] },
+  { path: 'pages/member/ranklist', title: '排行榜', domain: 'member', kind: 'resource', api: { action: 'ranklist_1' }, itemRoute: '/pages/shop/shop_item', emptyText: '暂无排行榜数据', tabs: ['周推广榜', '每周抢购', '平台热销', '会员热门', '最多回购'], tabActions: ['ranklist_1', 'ranklist_2', 'ranklist_3', 'ranklist_4', 'ranklist_5'] },
+  { path: 'pages/member/myshop', title: '我的商品', domain: 'member', kind: 'resource', auth: true, api: { action: 'product_list' }, itemRoute: '/pages/member/myshop_add', deleteAction: 'product_del', tabs: ['销售中', '待审核', '已停售'], tabActions: ['product_list', 'product_list', 'product_list'], tabParams: [{ mode: 'normal' }, { mode: 'unaudit' }, { mode: 'unsales' }], actions: [{ key: 'stop-sale', label: '暂停销售', action: 'product_sale_change', method: 'post', scope: 'item', params: { status: 0 } }, { key: 'start-sale', label: '重新销售', action: 'product_sale_change', method: 'post', scope: 'item', params: { status: 1 } }] },
+  { path: 'pages/member/myshop_add', title: '商品详情', domain: 'member', kind: 'form', auth: true, api: { action: 'product_get' }, saveAction: 'product_add', fields: [text('Proname', '商品名称', true), { key: 'Nodeid', label: '商品类别', type: 'select', required: true, options: [] }, text('LinPrice', '零售价格', true, 'number'), text('ShiPrice', '市场参照价', true, 'number'), text('ProUnit', '商品单位', true), text('Stock', '初始库存', true, 'number'), text('Thumbnails', '商品封面', true, 'file'), text('pics', '商品组图', true, 'file'), text('Proinfo', '商品简介', true, 'textarea'), text('Procontent', '商品介绍', true, 'textarea')] },
+  { path: 'pages/member/myshop_add_procon', title: '编辑商品介绍', domain: 'member', kind: 'form', auth: true, saveAction: 'product_upd', fields: [text('Procontent', '商品介绍', true, 'textarea'), text('image', '插入图片', false, 'file')] },
+  { path: 'pages/member/myshop_stock', title: '商品库存管理', domain: 'member', kind: 'merchant', auth: true },
+  { path: 'pages/member/myshop_sales', title: '销量统计', domain: 'member', kind: 'merchant', auth: true },
+  { path: 'pages/member/myshop_sales_log', title: '结算记录', domain: 'member', kind: 'merchant', auth: true },
+  { path: 'pages/member/myshop_order', title: '商户订单中心', domain: 'member', kind: 'resource', auth: true, api: { action: 'user_shop_order' }, itemRoute: '/pages/member/myshop_order_item', tabs: ['全部', '未发货', '待签收', '已完成'], tabActions: ['user_shop_order', 'user_shop_order', 'user_shop_order', 'user_shop_order'], tabParams: [{ fast: 'all' }, { fast: 'paid' }, { fast: 'issued' }, { fast: 'signed' }] },
+  { path: 'pages/member/myshop_order_item', title: '订单管理', domain: 'member', kind: 'order', auth: true },
+  { path: 'pages/member/feedback', title: '意见反馈与休假申请', domain: 'member', kind: 'form', auth: true, saveAction: 'guestbook_add', fields: [text('name', '您的姓名', true), content] },
+  { path: 'pages/member/starshop', title: '生词本', domain: 'member', kind: 'learning', auth: true },
+  { path: 'pages/member/passwd', title: '修改登录密码', domain: 'member', kind: 'form', auth: true, saveAction: 'user_update_pwd', fields: [text('passwd', '新密码', true, 'password'), text('passwd2', '确认密码', true, 'password'), text('vcode', '验证码', true)] },
+  { path: 'pages/member/passwd2', title: '修改支付密码', domain: 'member', kind: 'form', auth: true, saveAction: 'user_update_paypwd', fields: [text('paypwd', '新支付密码', true, 'password'), text('paypwd2', '确认支付密码', true, 'password'), text('vcode', '验证码', true)] },
+  { path: 'pages/member/rnauth', title: '实名认证', domain: 'member', kind: 'form', auth: true, api: { action: 'user_rnauth_get' }, saveAction: 'user_rnauth_add', fields: [text('realName', '真实姓名', true), text('idCard', '身份证件号', true), text('front', '身份证国徽面', true, 'file'), text('back', '身份证人像面', true, 'file'), text('handheld', '本人手持身份证照片', true, 'file'), text('mobile', '联系手机', false, 'tel'), text('address', '联系地址')] },
+  { path: 'pages/member/coin', title: '我的学习豆', domain: 'member', kind: 'finance', auth: true },
+  { path: 'pages/member/ghis', title: '抗遗忘历史', domain: 'member', kind: 'studyRecords', auth: true },
+  { path: 'pages/member/myke', title: '我的练习进度', domain: 'member', kind: 'studyRecords', auth: true },
+  { path: 'pages/member/ke_xiao', title: '销课记录', domain: 'member', kind: 'courseCenter', auth: true },
+  { path: 'pages/member/ke_fuxi', title: '复习收入', domain: 'member', kind: 'studyRecords', auth: true },
+];
+
+export const PAGE_BY_PATH = new Map(PAGE_CATALOG.map((page) => [page.path, page]));

+ 76 - 0
src/app/core/payment.service.ts

@@ -0,0 +1,76 @@
+import { Injectable } from '@angular/core';
+import { ApiError } from './api.service';
+
+export type PaymentProvider = 'alipay' | 'wxpay';
+export type PaymentResult = 'paid' | 'redirected';
+
+interface WeixinBridge {
+  invoke(name: string, payload: Record<string, unknown>, callback: (result: { err_msg?: string }) => void): void;
+}
+
+@Injectable({ providedIn: 'root' })
+export class PaymentService {
+  async pay(provider: PaymentProvider, raw: unknown): Promise<PaymentResult> {
+    const data = this.asRecord(raw);
+    if (provider === 'alipay') return this.alipay(data);
+    return this.wechat(data);
+  }
+
+  private async alipay(data: Record<string, unknown>): Promise<PaymentResult> {
+    const order = String(data['trade_no'] ?? data['orderInfo'] ?? '');
+    const redirect = String(data['mweb_url'] ?? data['url'] ?? (/^https?:\/\//i.test(order) ? order : ''));
+    if (redirect) {
+      window.location.assign(redirect);
+      return 'redirected';
+    }
+    if (/<form[\s>]/i.test(order)) {
+      const parsed = new DOMParser().parseFromString(order, 'text/html');
+      const source = parsed.querySelector('form');
+      if (!source?.action) throw new ApiError('支付宝返回的 H5 表单无效');
+      const form = document.createElement('form');
+      form.method = source.method || 'post';
+      form.action = source.action;
+      for (const element of Array.from(source.querySelectorAll('input[name]'))) {
+        const input = element as HTMLInputElement;
+        const hidden = document.createElement('input');
+        hidden.type = 'hidden';
+        hidden.name = input.name;
+        hidden.value = input.value;
+        form.appendChild(hidden);
+      }
+      document.body.appendChild(form);
+      form.submit();
+      return 'redirected';
+    }
+    throw new ApiError('当前支付单不包含可用于 Web 的支付宝参数');
+  }
+
+  private async wechat(data: Record<string, unknown>): Promise<PaymentResult> {
+    const redirect = String(data['mweb_url'] ?? data['h5_url'] ?? '');
+    if (redirect) {
+      window.location.assign(redirect);
+      return 'redirected';
+    }
+    const bridge = (window as Window & { WeixinJSBridge?: WeixinBridge }).WeixinJSBridge;
+    if (!bridge) throw new ApiError('请在微信内打开,或由后端返回微信 H5 支付链接');
+    return new Promise<PaymentResult>((resolve, reject) => {
+      bridge.invoke('getBrandWCPayRequest', {
+        appId: data['appid'] ?? data['appId'],
+        timeStamp: String(data['timeStamp'] ?? data['timestamp'] ?? ''),
+        nonceStr: data['nonceStr'] ?? data['noncestr'],
+        package: data['package'] ?? `prepay_id=${String(data['prepayid'] ?? data['prepayId'] ?? '')}`,
+        signType: data['signType'] ?? 'MD5',
+        paySign: data['paySign'] ?? data['sign'],
+      }, (result) => {
+        const status = String(result.err_msg ?? '');
+        if (status.endsWith(':ok')) resolve('paid');
+        else if (status.endsWith(':cancel')) reject(new ApiError('已取消支付'));
+        else reject(new ApiError('微信支付失败'));
+      });
+    });
+  }
+
+  private asRecord(value: unknown): Record<string, unknown> {
+    return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
+  }
+}

+ 52 - 0
src/app/core/platform.service.ts

@@ -0,0 +1,52 @@
+import { Injectable, signal } from '@angular/core';
+
+export interface ToastState {
+  message: string;
+  tone: 'info' | 'success' | 'error';
+}
+
+@Injectable({ providedIn: 'root' })
+export class PlatformService {
+  readonly loading = signal(false);
+  readonly toastState = signal<ToastState | null>(null);
+  private toastTimer: ReturnType<typeof setTimeout> | null = null;
+  private audio: HTMLAudioElement | null = null;
+
+  toast(message: string, tone: ToastState['tone'] = 'info'): void {
+    if (this.toastTimer) clearTimeout(this.toastTimer);
+    this.toastState.set({ message, tone });
+    this.toastTimer = setTimeout(() => this.toastState.set(null), 2600);
+  }
+
+  async confirm(message: string): Promise<boolean> {
+    return Promise.resolve(window.confirm(message));
+  }
+
+  async copy(text: string): Promise<void> {
+    await navigator.clipboard.writeText(text);
+    this.toast('已复制到剪贴板', 'success');
+  }
+
+  async share(data: ShareData): Promise<void> {
+    if (navigator.share) {
+      await navigator.share(data);
+      return;
+    }
+    await this.copy(data.url || data.text || data.title || '');
+  }
+
+  play(url: string): void {
+    this.stopAudio();
+    this.audio = new Audio(url);
+    void this.audio.play().catch(() => this.toast('音频播放失败', 'error'));
+  }
+
+  stopAudio(): void {
+    this.audio?.pause();
+    this.audio = null;
+  }
+
+  back(): void {
+    history.back();
+  }
+}

+ 89 - 0
src/app/core/session.service.ts

@@ -0,0 +1,89 @@
+import { computed, effect, Injectable, signal } from '@angular/core';
+import { AppUser } from './models';
+
+interface PersistedSession {
+  token: string;
+  user: AppUser | null;
+  student: AppUser | null;
+}
+
+const STORAGE_KEY = 'xiaoshu.angular.session';
+
+@Injectable({ providedIn: 'root' })
+export class SessionService {
+  readonly user = signal<AppUser | null>(null);
+  readonly token = signal('');
+  readonly student = signal<AppUser | null>(null);
+  readonly isLoggedIn = computed(() => Boolean(this.token() && this.user()?.userId));
+  readonly groupId = computed(() => Number(this.user()?.groupId ?? 0));
+
+  constructor() {
+    this.restore();
+    effect(() => {
+      const value: PersistedSession = {
+        token: this.token(),
+        user: this.user(),
+        student: this.student(),
+      };
+      localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
+    });
+  }
+
+  setLogin(user: AppUser): void {
+    this.user.set(user);
+    this.token.set(this.makeToken(user));
+  }
+
+  setStudent(student: AppUser | null): void {
+    this.student.set(student);
+  }
+
+  patchUser(patch: Partial<AppUser>): void {
+    const current = this.user();
+    if (current) {
+      this.user.set({ ...current, ...patch });
+    }
+  }
+
+  logout(): void {
+    this.user.set(null);
+    this.student.set(null);
+    this.token.set('');
+    localStorage.removeItem('lifeData');
+  }
+
+  private restore(): void {
+    const current = this.parse<PersistedSession>(localStorage.getItem(STORAGE_KEY));
+    if (current) {
+      this.user.set(current.user);
+      this.student.set(current.student);
+      this.token.set(current.token);
+      return;
+    }
+
+    const legacy = this.parse<Record<string, unknown>>(localStorage.getItem('lifeData'));
+    if (legacy) {
+      this.user.set((legacy['vuex_user'] as AppUser | null) ?? null);
+      this.student.set((legacy['vuex_stu_user'] as AppUser | null) ?? null);
+      this.token.set(String(legacy['vuex_token'] ?? ''));
+    }
+  }
+
+  private parse<T>(value: string | null): T | null {
+    if (!value) return null;
+    try {
+      return JSON.parse(value) as T;
+    } catch {
+      return null;
+    }
+  }
+
+  private makeToken(user: AppUser): string {
+    const source = `${user.userId}:${user.userName ?? ''}:${Date.now()}`;
+    let hash = 0;
+    for (let index = 0; index < source.length; index += 1) {
+      hash = (hash * 31 + source.charCodeAt(index)) >>> 0;
+    }
+    return `${user.userId}.${hash.toString(16)}`;
+  }
+}

+ 4168 - 0
src/app/core/source-parity.generated.ts

@@ -0,0 +1,4168 @@
+// Generated by npm run parity:generate. Do not edit manually.
+export const SOURCE_PAGE_PARITY = [
+  {
+    "path": "pages/home/index",
+    "sourceFile": "pages/home/index.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 341
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 384
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 403
+      },
+      {
+        "method": "post",
+        "action": "e_order_tongji",
+        "line": 433
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onShow",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "onBackPress",
+      "isRefersh",
+      "getCurrentVersion",
+      "getNewVersion",
+      "checkForUpdate",
+      "handleUpdateResponse",
+      "getv",
+      "getCurrentVersionCode",
+      "getRecommendList",
+      "getNoticeList",
+      "getSwiperList",
+      "addCart",
+      "viewShop",
+      "getTongji",
+      "addCartOk",
+      "box_if",
+      "authenticateUser",
+      "gotoExam",
+      "gotoLearningRecord"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 423
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 447
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 451
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/his/exam",
+        "line": 507
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/his/his",
+        "line": 514
+      }
+    ]
+  },
+  {
+    "path": "pages/home/ucenter",
+    "sourceFile": "pages/home/ucenter.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 290
+      },
+      {
+        "method": "get",
+        "action": "coupon_usrgot_list",
+        "line": 308
+      },
+      {
+        "method": "get",
+        "action": "order_list",
+        "line": 326
+      },
+      {
+        "method": "get",
+        "action": "order_list",
+        "line": 339
+      },
+      {
+        "method": "get",
+        "action": "order_list",
+        "line": 352
+      },
+      {
+        "method": "get",
+        "action": "order_list",
+        "line": 365
+      },
+      {
+        "method": "get",
+        "action": "order_list",
+        "line": 378
+      },
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 398
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onBackPress",
+      "onLoad",
+      "onShow",
+      "gotoHome",
+      "getMyShop",
+      "getCoupon",
+      "getOrder",
+      "getVip",
+      "ujump",
+      "logout",
+      "startCustomerService",
+      "shareApp",
+      "shareApp_CopyLink",
+      "shareApp_CopyCode",
+      "shareApp_SaveImg",
+      "call_phone"
+    ],
+    "navigations": [
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 279
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 464
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 472
+      },
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 491
+      }
+    ]
+  },
+  {
+    "path": "pages/home/shop",
+    "sourceFile": "pages/home/shop.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "user_get",
+        "line": 95
+      },
+      {
+        "method": "post",
+        "action": "user_get",
+        "line": 123
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 169
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "setStudentUser",
+      "setBaobiao",
+      "getShopList",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "onShow"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 85
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 108
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 117
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 136
+      }
+    ]
+  },
+  {
+    "path": "pages/home/cart",
+    "sourceFile": "pages/home/cart.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 78
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 106
+      },
+      {
+        "method": "get",
+        "action": "node_list",
+        "line": 136
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getCourseList",
+      "getPaikeList",
+      "loadList",
+      "toDetail",
+      "viewShop"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/shop/stu_get_order",
+        "line": 164
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 167
+      }
+    ]
+  },
+  {
+    "path": "pages/home/search",
+    "sourceFile": "pages/home/search.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 86
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onPullDownRefresh",
+      "onReachBottom",
+      "getList",
+      "addCart",
+      "addCartOk",
+      "viewShop"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 129
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 133
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 138
+      }
+    ]
+  },
+  {
+    "path": "pages/home/enjoy",
+    "sourceFile": "pages/home/enjoy.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 43
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 85
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "getList",
+      "viewDetail"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 114
+      }
+    ]
+  },
+  {
+    "path": "pages/home/enjoy_item",
+    "sourceFile": "pages/home/enjoy_item.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 45
+      },
+      {
+        "method": "post",
+        "action": "content_uphis",
+        "line": 64
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onUnload",
+      "getImgUrl",
+      "videoErrorCallback"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/home/help",
+    "sourceFile": "pages/home/help.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/home/webview",
+    "sourceFile": "pages/home/webview.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "components/bytedesk_kefu/chat-kf",
+    "sourceFile": "components/bytedesk_kefu/chat-kf.vue",
+    "apiCalls": [],
+    "externalCalls": [
+      {
+        "service": "bytedesk",
+        "operation": "requestThread",
+        "line": 779
+      },
+      {
+        "service": "bytedesk",
+        "operation": "requestWorkGroupThreadV2",
+        "line": 788
+      },
+      {
+        "service": "bytedesk",
+        "operation": "requestAgent",
+        "line": 806
+      },
+      {
+        "service": "bytedesk",
+        "operation": "requestThreadScan",
+        "line": 830
+      },
+      {
+        "service": "bytedesk",
+        "operation": "loadHistoryMessagesByTopic",
+        "line": 892
+      },
+      {
+        "service": "bytedesk",
+        "operation": "loadHistoryMessagesByTopic",
+        "line": 937
+      },
+      {
+        "service": "bytedesk",
+        "operation": "sendMessageRest",
+        "line": 1503
+      },
+      {
+        "service": "bytedesk",
+        "operation": "queryAnswer",
+        "line": 1864
+      },
+      {
+        "service": "bytedesk",
+        "operation": "queryCategoryAnswers",
+        "line": 1889
+      },
+      {
+        "service": "bytedesk",
+        "operation": "messageAnswer",
+        "line": 1917
+      },
+      {
+        "service": "bytedesk",
+        "operation": "rateAnswer",
+        "line": 1942
+      },
+      {
+        "service": "bytedesk",
+        "operation": "rateAnswer",
+        "line": 1959
+      },
+      {
+        "service": "bytedesk",
+        "operation": "getQuickButtons",
+        "line": 2187
+      },
+      {
+        "service": "bytedesk",
+        "operation": "getPrechatSettings",
+        "line": 2196
+      }
+    ],
+    "methods": [
+      "data",
+      "onLoad",
+      "onUnload",
+      "onShow",
+      "onReady",
+      "threadTopic",
+      "is_self",
+      "is_sending",
+      "is_stored",
+      "is_received",
+      "is_error",
+      "is_read",
+      "is_type_text",
+      "is_type_robot",
+      "is_type_robot_v2",
+      "is_type_robot_result",
+      "is_type_image",
+      "is_type_file",
+      "is_type_voice",
+      "is_type_video",
+      "is_type_location",
+      "is_type_link",
+      "is_type_event",
+      "is_type_custom",
+      "is_type_commodity",
+      "is_type_questionnaire",
+      "is_type_company",
+      "is_type_workGroup",
+      "is_type_form_request",
+      "is_type_form_result",
+      "is_type_thread",
+      "is_type_notification",
+      "is_type_close",
+      "is_type_notification_agent_close",
+      "is_type_notification_visitor_close",
+      "is_type_notification_auto_close",
+      "is_type_notification_thread_reentry",
+      "is_type_notification_connect",
+      "is_type_notification_disconnect",
+      "is_type_notification_offline",
+      "is_type_notification_invite_rate",
+      "is_type_notification_rate_result",
+      "is_type_notification_rate_helpful",
+      "is_type_notification_rate_helpless",
+      "is_type_notification_queue_accept",
+      "formatStatus",
+      "my_uid",
+      "my_nickname",
+      "thread_nickname",
+      "my_avatar",
+      "thread_avatar",
+      "jsonObject",
+      "scrollToBottom",
+      "scrollToMessage",
+      "requestThread",
+      "requestAgent",
+      "requestThreadScan",
+      "loadMoreMessages",
+      "loadHistoryMessagesByTopic",
+      "loadLatestMessage",
+      "dealWithThread",
+      "switchLeaveMessage",
+      "appendCommodityInfo",
+      "commodityInfo",
+      "onInputChange",
+      "sendTextMessageSync",
+      "sendImageMessageSync",
+      "sendFileMessageSync",
+      "sendVoiceMessageSync",
+      "sendVideoMessageSync",
+      "sendCommodityMessageSync",
+      "sendPreviewMessage",
+      "sendReceiptMessage",
+      "sendRecallMessage",
+      "sendTextMessage",
+      "doSendMessage",
+      "pushToMessageArray",
+      "byteDeskConnect",
+      "onMessageReceived",
+      "currentTimestamp",
+      "commodityCallback",
+      "chooseImage",
+      "camera",
+      "getImage",
+      "previewImageMessage",
+      "guid",
+      "appendQueryMessage",
+      "appendReplyMessage",
+      "queryAnswer",
+      "queryCategory",
+      "messageAnswer",
+      "rateAnswerHelpful",
+      "rateAnswerHelpless",
+      "showRatePage",
+      "gotoRatePage",
+      "showMore",
+      "openDrawer",
+      "hideDrawer",
+      "textareaFocus",
+      "playVoice",
+      "voiceBegin",
+      "recordBegin",
+      "voiceCancel",
+      "voiceIng",
+      "voiceEnd",
+      "recordEnd",
+      "switchVoice",
+      "discard",
+      "can_recall",
+      "callRecallMessage",
+      "longtap",
+      "closeTopTip",
+      "getQuickButtons",
+      "getPrechatSettings",
+      "checkTimeoutMessage",
+      "mounted",
+      "beforeDestroy"
+    ],
+    "navigations": [
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 1060
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 1980
+      }
+    ]
+  },
+  {
+    "path": "components/bytedesk_kefu/chat-im",
+    "sourceFile": "components/bytedesk_kefu/chat-im.vue",
+    "apiCalls": [],
+    "externalCalls": [
+      {
+        "service": "bytedesk",
+        "operation": "requestThread",
+        "line": 747
+      },
+      {
+        "service": "bytedesk",
+        "operation": "requestAgent",
+        "line": 763
+      },
+      {
+        "service": "bytedesk",
+        "operation": "requestThreadScan",
+        "line": 787
+      },
+      {
+        "service": "bytedesk",
+        "operation": "loadHistoryMessagesByTopic",
+        "line": 843
+      },
+      {
+        "service": "bytedesk",
+        "operation": "loadHistoryMessagesByTopic",
+        "line": 887
+      },
+      {
+        "service": "bytedesk",
+        "operation": "sendMessageRest",
+        "line": 1451
+      },
+      {
+        "service": "bytedesk",
+        "operation": "addBlock",
+        "line": 1646
+      },
+      {
+        "service": "bytedesk",
+        "operation": "markNodisturb",
+        "line": 1664
+      },
+      {
+        "service": "bytedesk",
+        "operation": "unmarkNodisturb",
+        "line": 1675
+      },
+      {
+        "service": "bytedesk",
+        "operation": "getOnlineAgents",
+        "line": 1686
+      },
+      {
+        "service": "bytedesk",
+        "operation": "agentCloseThread",
+        "line": 1858
+      },
+      {
+        "service": "bytedesk",
+        "operation": "queryAnswer",
+        "line": 1929
+      },
+      {
+        "service": "bytedesk",
+        "operation": "messageAnswer",
+        "line": 1958
+      }
+    ],
+    "methods": [
+      "data",
+      "onLoad",
+      "onUnload",
+      "onShow",
+      "onReady",
+      "threadTopic",
+      "is_self",
+      "is_sending",
+      "is_stored",
+      "is_received",
+      "is_error",
+      "is_read",
+      "is_type_text",
+      "is_type_robot",
+      "is_type_image",
+      "is_type_file",
+      "is_type_voice",
+      "is_type_video",
+      "is_type_location",
+      "is_type_link",
+      "is_type_event",
+      "is_type_custom",
+      "is_type_commodity",
+      "is_type_questionnaire",
+      "is_type_company",
+      "is_type_workGroup",
+      "is_type_form_request",
+      "is_type_form_result",
+      "is_type_thread",
+      "is_type_notification",
+      "is_type_close",
+      "is_type_notification_agent_close",
+      "is_type_notification_visitor_close",
+      "is_type_notification_auto_close",
+      "is_type_notification_thread_reentry",
+      "is_type_notification_connect",
+      "is_type_notification_disconnect",
+      "is_type_notification_offline",
+      "is_type_notification_invite_rate",
+      "is_type_notification_rate_result",
+      "is_type_notification_queue_accept",
+      "formatStatus",
+      "my_uid",
+      "my_username",
+      "my_nickname",
+      "thread_nickname",
+      "my_avatar",
+      "jsonObject",
+      "scrollToBottom",
+      "scrollToMessage",
+      "requestThread",
+      "requestAgent",
+      "requestThreadScan",
+      "loadMoreMessages",
+      "loadHistoryMessagesByTopic",
+      "loadLatestMessage",
+      "dealWithThread",
+      "appendCommodityInfo",
+      "commodityInfo",
+      "onInputChange",
+      "sendTextMessageSync",
+      "sendImageMessageSync",
+      "sendFileMessageSync",
+      "sendVoiceMessageSync",
+      "sendVideoMessageSync",
+      "sendCommodityMessageSync",
+      "sendPreviewMessage",
+      "sendReceiptMessage",
+      "sendRecallMessage",
+      "sendTextMessage",
+      "doSendMessage",
+      "pushToMessageArray",
+      "byteDeskConnect",
+      "onMessageReceived",
+      "currentTimestamp",
+      "addBlock",
+      "blockDialogChange",
+      "blockDialogInputConfirm",
+      "markNodisturb",
+      "unmarkNodisturb",
+      "getOnlineAgents",
+      "sendTransferMessage",
+      "acceptTransfer",
+      "rejectTransfer",
+      "inviteRate",
+      "closeThread",
+      "commodityCallback",
+      "chooseImage",
+      "camera",
+      "getImage",
+      "previewImageMessage",
+      "guid",
+      "queryAnswer",
+      "messageAnswer",
+      "showMore",
+      "openDrawer",
+      "hideDrawer",
+      "showCuw",
+      "textareaFocus",
+      "playVoice",
+      "voiceBegin",
+      "recordBegin",
+      "voiceCancel",
+      "voiceIng",
+      "voiceEnd",
+      "recordEnd",
+      "switchVoice",
+      "discard",
+      "can_recall",
+      "callRecallMessage",
+      "longtap",
+      "checkTimeoutMessage",
+      "mounted",
+      "beforeDestroy"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 2003
+      }
+    ]
+  },
+  {
+    "path": "components/bytedesk_kefu/rate",
+    "sourceFile": "components/bytedesk_kefu/rate.vue",
+    "apiCalls": [],
+    "externalCalls": [
+      {
+        "service": "bytedesk",
+        "operation": "rateDetail",
+        "line": 218
+      },
+      {
+        "service": "bytedesk",
+        "operation": "rate",
+        "line": 242
+      }
+    ],
+    "methods": [
+      "data",
+      "onLoad",
+      "formatScore",
+      "rateButtonText",
+      "setScore",
+      "clickButton1",
+      "clickButton2",
+      "clickButton3",
+      "clickButton4",
+      "clickButton5",
+      "clickButton6",
+      "clickButton7",
+      "blur",
+      "getRateDetail",
+      "doSubmit",
+      "created"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "components/bytedesk_kefu/webview",
+    "sourceFile": "components/bytedesk_kefu/webview.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "onLoad",
+      "data"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "components/bytedesk_kefu/leavemsg",
+    "sourceFile": "components/bytedesk_kefu/leavemsg.vue",
+    "apiCalls": [],
+    "externalCalls": [
+      {
+        "service": "bytedesk",
+        "operation": "saveLeaveMessage",
+        "line": 51
+      }
+    ],
+    "methods": [
+      "data",
+      "onLoad",
+      "subMit"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/stu/index",
+    "sourceFile": "pages/stu/index.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_list",
+        "line": 176
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 215
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "loadList",
+      "getCourseList",
+      "viewShop"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 221
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu_list",
+    "sourceFile": "pages/stu/stu_list.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_list",
+        "line": 81
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 141
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "showList",
+      "getNlist",
+      "goToAbc",
+      "gotoHome",
+      "checkboxChange",
+      "checkedThis",
+      "markerComplate",
+      "confirmMark",
+      "cancelMark",
+      "getUnitRecord",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_con",
+        "line": 74
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 99
+      },
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 102
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu_con",
+    "sourceFile": "pages/stu/stu_con.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_list",
+        "line": 257
+      },
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 268
+      },
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 364
+      },
+      {
+        "method": "post",
+        "action": "content_add",
+        "line": 398
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "step",
+      "onReady",
+      "actionClick",
+      "getNlist",
+      "makeWordToPage",
+      "checkComplate",
+      "skipThis",
+      "nextStep",
+      "sortWordList",
+      "saveComplateStorage",
+      "getNodeList",
+      "readWords",
+      "playAudio",
+      "playSuccessAudio",
+      "savePlayRecord",
+      "getStoragePlayRecord",
+      "getContent",
+      "recordComplateInfo",
+      "showNextUnit",
+      "wordShuffle",
+      "setNavBarTitle",
+      "onShow",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 146
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu3",
+        "line": 205
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu3",
+        "line": 210
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_con",
+        "line": 408
+      }
+    ]
+  },
+  {
+    "path": "pages/game/game",
+    "sourceFile": "pages/game/game.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "wrongWords",
+      "mounted",
+      "beforeDestroy",
+      "createWords",
+      "onHandleKey",
+      "showWords",
+      "init",
+      "gameOver",
+      "drawMan"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/game/index",
+    "sourceFile": "pages/game/index.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "gotoHome",
+      "isLeave"
+    ],
+    "navigations": [
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 34
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu_abc",
+    "sourceFile": "pages/stu/stu_abc.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "e_ck_list",
+        "line": 123
+      },
+      {
+        "method": "post",
+        "action": "e_add_words",
+        "line": 185
+      },
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 285
+      },
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 294
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "onShow",
+      "checkboxChange",
+      "checkedThis",
+      "checkAll",
+      "getContentList",
+      "startLearn",
+      "saveChooseWord",
+      "checkChoosedWord",
+      "readWords",
+      "getWords",
+      "getContent",
+      "addUnitRecord",
+      "gotoHome",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 209
+      },
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 306
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu3",
+    "sourceFile": "pages/stu/stu3.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 97
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "showList",
+      "getNlist",
+      "leftWordClick",
+      "compareIndex",
+      "complete",
+      "checkComplate",
+      "playAgain",
+      "skipThis",
+      "rightWordClick",
+      "readWords",
+      "playSuccessAudio",
+      "onShow",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_con",
+        "line": 68
+      },
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 83
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu4",
+        "line": 168
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu4",
+    "sourceFile": "pages/stu/stu4.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 75
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 116
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "getRandWrodList",
+      "getContentList",
+      "readWords",
+      "playAudio",
+      "playSuccessAudio",
+      "chooseTranslate",
+      "checkComplate",
+      "playAgain",
+      "skipThis",
+      "onShow",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 102
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/pl_stu5",
+        "line": 252
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu5",
+        "line": 255
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu5",
+    "sourceFile": "pages/stu/stu5.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 237
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "wordIndex",
+      "onReady",
+      "spellWord",
+      "checkSpell",
+      "complete",
+      "spellAgain",
+      "splitWord",
+      "nextWord",
+      "checkComplate",
+      "playSuccessAudio",
+      "playAgain",
+      "skipThis",
+      "getNlist",
+      "readWords",
+      "onShow",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_con",
+        "line": 193
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "pages/member/starshop",
+        "line": 198
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "pages/his/his",
+        "line": 201
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu6",
+        "line": 204
+      },
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 222
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu6",
+    "sourceFile": "pages/stu/stu6.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 227
+      },
+      {
+        "method": "post",
+        "action": "unit_record_update",
+        "line": 276
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 293
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "readWords",
+      "showList",
+      "getNlist",
+      "checkThisWord",
+      "checkAll",
+      "createLearnRecord",
+      "learnComplate",
+      "complete",
+      "updateUnitRecord",
+      "getTodayLearnedRecord",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_con",
+        "line": 93
+      },
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 115
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/member/myorder",
+        "line": 247
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu_ok",
+    "sourceFile": "pages/stu/stu_ok.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "gotoHome"
+    ],
+    "navigations": [
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 30
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu_calendar",
+    "sourceFile": "pages/stu/stu_calendar.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "e_get_21list",
+        "line": 59
+      },
+      {
+        "method": "post",
+        "action": "user_get",
+        "line": 111
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getGuyiList",
+      "formatDataList",
+      "toLearn",
+      "onReachBottom"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/record",
+        "line": 124
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/stu7",
+    "sourceFile": "pages/stu/stu7.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 114
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 128
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 156
+      },
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 191
+      },
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 205
+      },
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 234
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 314
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 367
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "checkboxChange",
+      "joinWordPaper",
+      "getMyNewWord",
+      "getListByDate",
+      "getRecordInfo",
+      "getGuyiInfo",
+      "getContent",
+      "readWords",
+      "playAudio",
+      "checkRecordComplate",
+      "checkRecordComplate__bak",
+      "checkComplate",
+      "onLoad"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/home/app_update",
+    "sourceFile": "pages/home/app_update.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "app_update",
+        "line": 73
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onBackPress",
+      "confirm",
+      "update",
+      "install",
+      "cancel"
+    ],
+    "navigations": [
+      {
+        "call": "uni.reLaunch",
+        "target": "<dynamic>",
+        "line": 58
+      }
+    ]
+  },
+  {
+    "path": "pages/his/his",
+    "sourceFile": "pages/his/his.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 161
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onShow",
+      "onReachBottom",
+      "confirmDate",
+      "openCalendar",
+      "showRecord",
+      "toLearn",
+      "startLearn",
+      "chooseStuUser",
+      "initRecordList",
+      "getRecordList",
+      "processXxqsData",
+      "goToIframePage"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/his/his_words",
+        "line": 114
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 124
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 130
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 272
+      }
+    ]
+  },
+  {
+    "path": "pages/game/wam",
+    "sourceFile": "pages/game/wam.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "da",
+      "sjs",
+      "gotoHome",
+      "onShow"
+    ],
+    "navigations": [
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 66
+      }
+    ]
+  },
+  {
+    "path": "pages/game/plane",
+    "sourceFile": "pages/game/plane.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "touchStart",
+      "touchEnd",
+      "keyDown",
+      "leftBtn",
+      "rightBtn",
+      "getDomXY",
+      "timsetIntval",
+      "zidanBtnadd",
+      "dijiAdd",
+      "dijixing",
+      "getDomHeight",
+      "closeTime",
+      "gotoHome",
+      "onLoad",
+      "onReady"
+    ],
+    "navigations": [
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 180
+      }
+    ]
+  },
+  {
+    "path": "pages/game/downletter",
+    "sourceFile": "pages/game/downletter.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [],
+    "navigations": []
+  },
+  {
+    "path": "pages/his/his_words",
+    "sourceFile": "pages/his/his_words.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "e_record_detail",
+        "line": 113
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "checkboxChange",
+      "learnWords",
+      "checkedThis",
+      "checkAll",
+      "getContentList",
+      "startLearn",
+      "readWords",
+      "addUnitRecord",
+      "gotoHome",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 171
+      },
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 207
+      }
+    ]
+  },
+  {
+    "path": "pages/his/exam",
+    "sourceFile": "pages/his/exam.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "vote_ask",
+        "line": 413
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 425
+      },
+      {
+        "method": "get",
+        "action": "vote_question",
+        "line": 442
+      },
+      {
+        "method": "post",
+        "action": "vote_add",
+        "line": 577
+      },
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 671
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "current",
+      "onReady",
+      "onLoad",
+      "getServerData",
+      "turnAskId",
+      "startAsk",
+      "getAskInfo",
+      "getPrevExam",
+      "getQuestionList",
+      "wordShuffle",
+      "formatQuestion",
+      "checkThis",
+      "redirectThis",
+      "showQuestionList",
+      "prevQuestion",
+      "nextQuestion",
+      "calcAnserResult",
+      "submit",
+      "goToIframePage",
+      "addExamLog",
+      "close",
+      "startTimer",
+      "playTimer",
+      "open"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 618
+      }
+    ]
+  },
+  {
+    "path": "pages/iframe/index",
+    "sourceFile": "pages/iframe/index.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "handleDownload",
+      "handleBack",
+      "downloadPdf",
+      "downloadPdfFallback"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/his/his",
+        "line": 75
+      }
+    ]
+  },
+  {
+    "path": "pages/game/llk",
+    "sourceFile": "pages/game/llk.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 134
+      },
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 150
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 215
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 236
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 256
+      },
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 300
+      },
+      {
+        "method": "post",
+        "action": "user_update",
+        "line": 339
+      },
+      {
+        "method": "get",
+        "action": "content_list_llk",
+        "line": 381
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "current",
+      "gameDay",
+      "onLoad",
+      "initView",
+      "startTimecalc",
+      "calcTime",
+      "getUnitList",
+      "getNodeInfo",
+      "gameStart",
+      "toggleThis",
+      "checkClick",
+      "getRecord",
+      "addRecord",
+      "gameOver",
+      "updateUserPosition",
+      "playSuccessAudio",
+      "getContentList"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/his/well",
+    "sourceFile": "pages/his/well.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 62
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 105
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "getList",
+      "viewDetail",
+      "click"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 133
+      }
+    ]
+  },
+  {
+    "path": "pages/help/help",
+    "sourceFile": "pages/help/help.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 64
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 107
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "getList",
+      "viewDetail",
+      "click"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 135
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/pl_stu5",
+    "sourceFile": "pages/stu/pl_stu5.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "stu_record_update_v2",
+        "line": 284
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 297
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 334
+      },
+      {
+        "method": "get",
+        "action": "e_order_detail",
+        "line": 387
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "wordIndex",
+      "onReady",
+      "spellWord",
+      "checkSpell",
+      "complete",
+      "spellAgain",
+      "splitWord",
+      "nextWord",
+      "checkComplate",
+      "playSuccessAudio",
+      "playAgain",
+      "skipThis",
+      "createLearnRecord",
+      "getLearnedRecordByOrderId",
+      "getNlist",
+      "readWords",
+      "getOrderDetail",
+      "onShow",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_con",
+        "line": 195
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "pages/member/starshop",
+        "line": 200
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "pages/his/his",
+        "line": 203
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/pl_end",
+        "line": 288
+      },
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 319
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/pl_end",
+    "sourceFile": "pages/stu/pl_end.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "stu_record_update_v2",
+        "line": 180
+      },
+      {
+        "method": "post",
+        "action": "e_order_update_v2",
+        "line": 199
+      },
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 248
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 263
+      },
+      {
+        "method": "get",
+        "action": "e_order_detail",
+        "line": 274
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 284
+      },
+      {
+        "method": "get",
+        "action": "e_record_detail",
+        "line": 287
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "checkAll",
+      "checkThisWord",
+      "readWords",
+      "playAgain",
+      "beforeOrderEnd",
+      "orderEnd",
+      "createLearnRecord",
+      "getTodayPlRecord",
+      "getOrderDetail",
+      "getLearnedRecordByOrderId",
+      "skipThis",
+      "onShow",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/shop/get_order",
+        "line": 121
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/shop/get_order",
+        "line": 253
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_con",
+        "line": 313
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "pages/member/starshop",
+        "line": 318
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "pages/his/his",
+        "line": 321
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/shop/get_order",
+        "line": 325
+      }
+    ]
+  },
+  {
+    "path": "pages/stu/record",
+    "sourceFile": "pages/stu/record.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "e_record_detail",
+        "line": 57
+      },
+      {
+        "method": "post",
+        "action": "e_add_words",
+        "line": 108
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 129
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getRecordDetail",
+      "checkThisWord",
+      "addNewWord",
+      "endStudy",
+      "readWords"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu_calendar",
+        "line": 132
+      }
+    ]
+  },
+  {
+    "path": "pages/his/his_stu",
+    "sourceFile": "pages/his/his_stu.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "e_user_list",
+        "line": 69
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onShow",
+      "onReachBottom",
+      "showRecord",
+      "chooseUser",
+      "getRecordList"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/his/his_words",
+        "line": 53
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/shop_item",
+    "sourceFile": "pages/shop/shop_item.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "product_get",
+        "line": 254
+      },
+      {
+        "method": "get",
+        "action": "order_comment_list",
+        "line": 392
+      },
+      {
+        "method": "get",
+        "action": "user_star_is",
+        "line": 417
+      },
+      {
+        "method": "post",
+        "action": "user_star_add",
+        "line": 440
+      },
+      {
+        "method": "post",
+        "action": "user_star_del",
+        "line": 467
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReady",
+      "onPageScroll",
+      "onBackPress",
+      "addCart",
+      "addCartOk",
+      "addCartErr",
+      "previewImage",
+      "loadComments",
+      "starIf",
+      "star",
+      "unstar",
+      "shareNormal",
+      "shareImage",
+      "loadRect",
+      "scrollRect",
+      "viewImage",
+      "back"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 371
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 375
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/shop_item_comment",
+    "sourceFile": "pages/shop/shop_item_comment.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "order_comment_list",
+        "line": 118
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onPullDownRefresh",
+      "getList",
+      "viewImage"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/shop/shop_class",
+    "sourceFile": "pages/shop/shop_class.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 64
+      },
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 113
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "onBackPress",
+      "getShopList",
+      "addCart",
+      "viewShop",
+      "addCartOk"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 148
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 160
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 164
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/shop_nodes",
+    "sourceFile": "pages/shop/shop_nodes.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 74
+      },
+      {
+        "method": "get",
+        "action": "node_list",
+        "line": 115
+      },
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 150
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onShow",
+      "onBackPress",
+      "getNodeList",
+      "getShopList",
+      "nodeSelect",
+      "addCart",
+      "viewShop",
+      "addCartOk"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/member/wxauth",
+        "line": 203
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/member/coin?charge=1",
+        "line": 210
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/member/wxauth",
+        "line": 230
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/member/coin?charge=1",
+        "line": 237
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 250
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 262
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 266
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/get_order",
+    "sourceFile": "pages/shop/get_order.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_list",
+        "line": 136
+      },
+      {
+        "method": "post",
+        "action": "e_order_update_v2",
+        "line": 169
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 196
+      },
+      {
+        "method": "post",
+        "action": "content_list",
+        "line": 226
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 279
+      },
+      {
+        "method": "post",
+        "action": "e_order_update_v2",
+        "line": 297
+      },
+      {
+        "method": "post",
+        "action": "e_order_update_v2",
+        "line": 325
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 384
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 411
+      },
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 495
+      },
+      {
+        "method": "get",
+        "action": "e_order_detail",
+        "line": 513
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 537
+      },
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 594
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 607
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "doChangePicker",
+      "getNlist",
+      "goToAbc",
+      "getStuOrderList",
+      "getNodeProcess",
+      "picKerEnd",
+      "changeOrderKcId",
+      "orderEnd",
+      "complateOrder",
+      "gotoHome",
+      "checkboxChange",
+      "checkedThis",
+      "markerComplate",
+      "commentThisOrder",
+      "confirmMark",
+      "cancelMark",
+      "createLearnRecord",
+      "getOrderDetail",
+      "getTodayPlRecord",
+      "createStuLearnRecord",
+      "getLearnedRecordByOrderId",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 180
+      },
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 339
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/get_order_vip",
+    "sourceFile": "pages/shop/get_order_vip.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "cart_list",
+        "line": 361
+      },
+      {
+        "method": "get",
+        "action": "coupon_usrgot_list",
+        "line": 399
+      },
+      {
+        "method": "get",
+        "action": "receaddr_list",
+        "line": 419
+      },
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 435
+      },
+      {
+        "method": "post",
+        "action": "payment_cart",
+        "line": 489
+      },
+      {
+        "method": "post",
+        "action": "payment_success",
+        "line": 503
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onBackPress",
+      "onLoad",
+      "onShow",
+      "settle",
+      "chooseAddress",
+      "chooseCoupon",
+      "clearCoupon",
+      "saveInvoice",
+      "chooseInvoice"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/member/coin?charge=1",
+        "line": 478
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 512
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 622
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/order",
+    "sourceFile": "pages/shop/order.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "order_get",
+        "line": 299
+      },
+      {
+        "method": "post",
+        "action": "payment_cart_again",
+        "line": 412
+      },
+      {
+        "method": "post",
+        "action": "payment_success",
+        "line": 425
+      },
+      {
+        "method": "post",
+        "action": "payment_success",
+        "line": 448
+      },
+      {
+        "method": "post",
+        "action": "payment_success",
+        "line": 484
+      },
+      {
+        "method": "post",
+        "action": "order_signfor",
+        "line": 530
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onBackPress",
+      "onLoad",
+      "onUnload",
+      "settle",
+      "signfor",
+      "appeal",
+      "copyExpNo"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 545
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/order_appeal",
+    "sourceFile": "pages/shop/order_appeal.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "order_get",
+        "line": 113
+      },
+      {
+        "method": "get",
+        "action": "pub_list",
+        "line": 152
+      },
+      {
+        "method": "get",
+        "action": "pub_list",
+        "line": 161
+      },
+      {
+        "method": "post",
+        "action": "pub_add",
+        "line": 263
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "uploadImg",
+      "delImg",
+      "save",
+      "back",
+      "viewImage"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 279
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/order_comment",
+    "sourceFile": "pages/shop/order_comment.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "order_get",
+        "line": 65
+      },
+      {
+        "method": "get",
+        "action": "order_comment_list",
+        "line": 131
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onPullDownRefresh",
+      "getList",
+      "add"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 163
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/order_comment_add",
+    "sourceFile": "pages/shop/order_comment_add.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "order_get",
+        "line": 117
+      },
+      {
+        "method": "post",
+        "action": "order_comment_add",
+        "line": 228
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "uploadImg",
+      "delImg",
+      "viewImage",
+      "save",
+      "back"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/shop/coupon",
+    "sourceFile": "pages/shop/coupon.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "coupon_list",
+        "line": 47
+      },
+      {
+        "method": "post",
+        "action": "coupon_usrgot_add",
+        "line": 82
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "getList",
+      "receive"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/shop/stu_get_order",
+    "sourceFile": "pages/shop/stu_get_order.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "e_order_update_v2",
+        "line": 108
+      },
+      {
+        "method": "post",
+        "action": "content_update",
+        "line": 134
+      },
+      {
+        "method": "get",
+        "action": "e_order_detail",
+        "line": 153
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 167
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "confirmEndOrder",
+      "orderEnd",
+      "commentThisOrder",
+      "gotoHome",
+      "getOrderDetail",
+      "getTodayPlRecord",
+      "onLoad"
+    ],
+    "navigations": [
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 145
+      }
+    ]
+  },
+  {
+    "path": "pages/shop/baobiao",
+    "sourceFile": "pages/shop/baobiao.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "copy"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/point",
+    "sourceFile": "pages/member/point.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_point_list",
+        "line": 126
+      },
+      {
+        "method": "post",
+        "action": "user_exp_transfer",
+        "line": 189
+      },
+      {
+        "method": "post",
+        "action": "user_exp_transfer",
+        "line": 207
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onBackPress",
+      "getList",
+      "transferMoney"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/purse",
+    "sourceFile": "pages/member/purse.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_point_list",
+        "line": 250
+      },
+      {
+        "method": "post",
+        "action": "user_money_recharge",
+        "line": 309
+      },
+      {
+        "method": "post",
+        "action": "user_money_transfer",
+        "line": 405
+      },
+      {
+        "method": "post",
+        "action": "user_money_transfer",
+        "line": 424
+      },
+      {
+        "method": "post",
+        "action": "user_cash_add",
+        "line": 491
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onBackPress",
+      "getList",
+      "chargeMoney",
+      "transferMoney",
+      "outMoney",
+      "outMoney_accsel"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 518
+      }
+    ]
+  },
+  {
+    "path": "pages/member/purse_outacc",
+    "sourceFile": "pages/member/purse_outacc.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_bank_list",
+        "line": 49
+      },
+      {
+        "method": "post",
+        "action": "user_bank_del",
+        "line": 93
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onPullDownRefresh",
+      "loadList",
+      "add",
+      "edit",
+      "select",
+      "remove"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 64
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 75
+      }
+    ]
+  },
+  {
+    "path": "pages/member/purse_outacc_add",
+    "sourceFile": "pages/member/purse_outacc_add.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_bank_get",
+        "line": 91
+      },
+      {
+        "method": "post",
+        "action": "user_bank_add",
+        "line": 150
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "save",
+      "back",
+      "acctypSelectConfirm"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/purse_out_log",
+    "sourceFile": "pages/member/purse_out_log.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_cash_list",
+        "line": 80
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "getList"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/settings",
+    "sourceFile": "pages/member/settings.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 140
+      },
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 153
+      },
+      {
+        "method": "post",
+        "action": "user_update",
+        "line": 246
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "changeFaceImage",
+      "birthdayPickerConfirm",
+      "save",
+      "back"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/wxauth",
+    "sourceFile": "pages/member/wxauth.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "user_info_name",
+        "line": 220
+      },
+      {
+        "method": "post",
+        "action": "user_register",
+        "line": 227
+      },
+      {
+        "method": "post",
+        "action": "user_login_passwd",
+        "line": 276
+      },
+      {
+        "method": "post",
+        "action": "mcode_send",
+        "line": 329
+      },
+      {
+        "method": "post",
+        "action": "user_login_mobile",
+        "line": 375
+      },
+      {
+        "method": "post",
+        "action": "user_register_mobile",
+        "line": 429
+      },
+      {
+        "method": "post",
+        "action": "user_info_name",
+        "line": 492
+      },
+      {
+        "method": "post",
+        "action": "user_register",
+        "line": 510
+      },
+      {
+        "method": "post",
+        "action": "user_sync2",
+        "line": 630
+      },
+      {
+        "method": "post",
+        "action": "user_sync2",
+        "line": 668
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onReady",
+      "privacyCheck",
+      "switchLoginMode",
+      "classic_SwitchRegister",
+      "doClassicRegister",
+      "doClassicLogin",
+      "mobile_SwitchRegister",
+      "mobile_GetVcode",
+      "doMobileLogin",
+      "doMobileRegister",
+      "doUniverifyLogin",
+      "doWeixinAppLogin",
+      "doWeixinMpLogin"
+    ],
+    "navigations": [
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 241
+      },
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 291
+      },
+      {
+        "call": "uni.switchTab",
+        "target": "<dynamic>",
+        "line": 389
+      },
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 440
+      }
+    ]
+  },
+  {
+    "path": "pages/member/wxauth_after",
+    "sourceFile": "pages/member/wxauth_after.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "user_update_pwdall",
+        "line": 84
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onBackPress",
+      "save"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/receaddr",
+    "sourceFile": "pages/member/receaddr.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "receaddr_list",
+        "line": 48
+      },
+      {
+        "method": "post",
+        "action": "receaddr_del",
+        "line": 65
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onShow",
+      "onPullDownRefresh",
+      "loadAddressList",
+      "delAddress",
+      "updAddress"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 77
+      }
+    ]
+  },
+  {
+    "path": "pages/member/receaddr_add",
+    "sourceFile": "pages/member/receaddr_add.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "receaddr_get",
+        "line": 107
+      },
+      {
+        "method": "post",
+        "action": "receaddr_upd",
+        "line": 213
+      },
+      {
+        "method": "post",
+        "action": "receaddr_add",
+        "line": 222
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "save",
+      "back",
+      "addrSelectorChange",
+      "chooseAddress",
+      "chooseAddressOk"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/invoice",
+    "sourceFile": "pages/member/invoice.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "invoice_list",
+        "line": 91
+      },
+      {
+        "method": "post",
+        "action": "invoice_del",
+        "line": 124
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onShow",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "getInvoiceList",
+      "delInvoice",
+      "updInvoice",
+      "selInvoice"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 140
+      }
+    ]
+  },
+  {
+    "path": "pages/member/invoice_add",
+    "sourceFile": "pages/member/invoice_add.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "invoice_get",
+        "line": 144
+      },
+      {
+        "method": "post",
+        "action": "invoice_upd",
+        "line": 245
+      },
+      {
+        "method": "post",
+        "action": "invoice_add",
+        "line": 254
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "save",
+      "back"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/myorder",
+    "sourceFile": "pages/member/myorder.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 88
+      },
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 208
+      },
+      {
+        "method": "get",
+        "action": "node_get",
+        "line": 211
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReady",
+      "getRecordList",
+      "processXxqsData",
+      "goToIframePage",
+      "showRecord",
+      "getNodeInfo",
+      "onReachBottom"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 196
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/his/his_words",
+        "line": 201
+      }
+    ]
+  },
+  {
+    "path": "pages/member/myorder_appeal",
+    "sourceFile": "pages/member/myorder_appeal.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "pub_list",
+        "line": 145
+      },
+      {
+        "method": "get",
+        "action": "pub_list",
+        "line": 203
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getAppeal_returns",
+      "getAppeal_exchanges",
+      "viewImage"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/spread",
+    "sourceFile": "pages/member/spread.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 82
+      },
+      {
+        "method": "get",
+        "action": "user_dept",
+        "line": 126
+      },
+      {
+        "method": "get",
+        "action": "user_list",
+        "line": 144
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReady",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "loadVips",
+      "getList"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/vip",
+    "sourceFile": "pages/member/vip.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 77
+      },
+      {
+        "method": "get",
+        "action": "user_list",
+        "line": 88
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReady"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/group",
+    "sourceFile": "pages/member/group.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 73
+      },
+      {
+        "method": "get",
+        "action": "user_list",
+        "line": 85
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReady"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/group_detail",
+    "sourceFile": "pages/member/group_detail.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 125
+      },
+      {
+        "method": "get",
+        "action": "user_list",
+        "line": 168
+      },
+      {
+        "method": "post",
+        "action": "user_group_usr_upgrade",
+        "line": 229
+      },
+      {
+        "method": "post",
+        "action": "user_group_usr_supply",
+        "line": 277
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onPullDownRefresh",
+      "onReachBottom",
+      "getList",
+      "upgradeBefore",
+      "upgrade",
+      "supplyBefore",
+      "supply"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/acc_destory",
+    "sourceFile": "pages/member/acc_destory.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "pub_list",
+        "line": 47
+      },
+      {
+        "method": "post",
+        "action": "pub_add",
+        "line": 71
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "save",
+      "back"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/mycoupon",
+    "sourceFile": "pages/member/mycoupon.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "coupon_usrgot_list",
+        "line": 108
+      },
+      {
+        "method": "get",
+        "action": "coupon_usrgot_list",
+        "line": 127
+      },
+      {
+        "method": "get",
+        "action": "coupon_usrgot_list",
+        "line": 146
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onPullDownRefresh",
+      "loadExpiredList",
+      "loadIdleList",
+      "loadUsedList"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/group_join",
+    "sourceFile": "pages/member/group_join.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 149
+      },
+      {
+        "method": "post",
+        "action": "pub_add",
+        "line": 187
+      },
+      {
+        "method": "post",
+        "action": "pub_add",
+        "line": 239
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onBackPress",
+      "onLoad",
+      "openForm",
+      "saveForm",
+      "openInvi",
+      "saveInvi",
+      "uploadImg",
+      "delImg"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/ranklist",
+    "sourceFile": "pages/member/ranklist.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "<dynamic>",
+        "line": 175
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "loadList",
+      "viewShop"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 202
+      }
+    ]
+  },
+  {
+    "path": "pages/member/myshop",
+    "sourceFile": "pages/member/myshop.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 207
+      },
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 256
+      },
+      {
+        "method": "get",
+        "action": "product_list",
+        "line": 305
+      },
+      {
+        "method": "post",
+        "action": "product_del",
+        "line": 348
+      },
+      {
+        "method": "post",
+        "action": "product_sale_change",
+        "line": 401
+      },
+      {
+        "method": "post",
+        "action": "product_sale_change",
+        "line": 428
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getShop_unaudit",
+      "getShop_normal",
+      "getShop_unsales",
+      "delShop",
+      "editShop",
+      "stopSaleShop",
+      "startSaleShop",
+      "mgrShopStock"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 381
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 450
+      }
+    ]
+  },
+  {
+    "path": "pages/member/myshop_add",
+    "sourceFile": "pages/member/myshop_add.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "node_list",
+        "line": 153
+      },
+      {
+        "method": "get",
+        "action": "product_get",
+        "line": 191
+      },
+      {
+        "method": "post",
+        "action": "<dynamic>",
+        "line": 251
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "saveSecoundStage",
+      "save",
+      "back",
+      "nodePrickerConfirm",
+      "editProcontent",
+      "updCover",
+      "delCover",
+      "uploadImg",
+      "delImg",
+      "editStock"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 349
+      },
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 437
+      }
+    ]
+  },
+  {
+    "path": "pages/member/myshop_add_procon",
+    "sourceFile": "pages/member/myshop_add_procon.vue",
+    "apiCalls": [],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReady",
+      "save",
+      "back",
+      "inserImage"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/myshop_stock",
+    "sourceFile": "pages/member/myshop_stock.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "product_get",
+        "line": 123
+      },
+      {
+        "method": "get",
+        "action": "product_stock_list",
+        "line": 171
+      },
+      {
+        "method": "post",
+        "action": "product_stock_change",
+        "line": 230
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onBackPress",
+      "getList",
+      "startDecrease",
+      "startIncrease",
+      "changeStock"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/myshop_sales",
+    "sourceFile": "pages/member/myshop_sales.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "pub_list",
+        "line": 138
+      },
+      {
+        "method": "get",
+        "action": "user_shop_sales",
+        "line": 156
+      },
+      {
+        "method": "get",
+        "action": "user_shop_sales",
+        "line": 170
+      },
+      {
+        "method": "get",
+        "action": "user_shop_sales",
+        "line": 185
+      },
+      {
+        "method": "post",
+        "action": "pub_add",
+        "line": 212
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getSales_week",
+      "getSales_month",
+      "getSales_all",
+      "saveSettle",
+      "viewShop"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 235
+      }
+    ]
+  },
+  {
+    "path": "pages/member/myshop_sales_log",
+    "sourceFile": "pages/member/myshop_sales_log.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "pub_list",
+        "line": 59
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "getList"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/myshop_order",
+    "sourceFile": "pages/member/myshop_order.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_shop_order",
+        "line": 255
+      },
+      {
+        "method": "get",
+        "action": "user_shop_order",
+        "line": 304
+      },
+      {
+        "method": "get",
+        "action": "user_shop_order",
+        "line": 353
+      },
+      {
+        "method": "get",
+        "action": "user_shop_order",
+        "line": 402
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getUshop_all",
+      "getUshop_paid",
+      "getUshop_issued",
+      "getUshop_signed",
+      "viewShop",
+      "viewOrder"
+    ],
+    "navigations": [
+      {
+        "call": "uni.navigateTo",
+        "target": "<dynamic>",
+        "line": 441
+      }
+    ]
+  },
+  {
+    "path": "pages/member/myshop_order_item",
+    "sourceFile": "pages/member/myshop_order_item.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "order_get",
+        "line": 287
+      },
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 328
+      },
+      {
+        "method": "post",
+        "action": "order_delivery",
+        "line": 371
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "delivery",
+      "back"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/feedback",
+    "sourceFile": "pages/member/feedback.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "guestbook_add",
+        "line": 76
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "save",
+      "back"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/starshop",
+    "sourceFile": "pages/member/starshop.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "e_words_list",
+        "line": 161
+      },
+      {
+        "method": "post",
+        "action": "e_add_words",
+        "line": 208
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onShow",
+      "onPullDownRefresh",
+      "onReachBottom",
+      "checkboxChange",
+      "getContent",
+      "startLearn",
+      "getMyNewWord",
+      "deleteNewWord",
+      "readWords",
+      "playAudio"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 146
+      }
+    ]
+  },
+  {
+    "path": "pages/member/passwd",
+    "sourceFile": "pages/member/passwd.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "mcode_send",
+        "line": 79
+      },
+      {
+        "method": "post",
+        "action": "user_update_pwd",
+        "line": 130
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "getVCode",
+      "save",
+      "back"
+    ],
+    "navigations": [
+      {
+        "call": "uni.reLaunch",
+        "target": "<dynamic>",
+        "line": 58
+      },
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 67
+      }
+    ]
+  },
+  {
+    "path": "pages/member/passwd2",
+    "sourceFile": "pages/member/passwd2.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "mcode_send",
+        "line": 79
+      },
+      {
+        "method": "post",
+        "action": "user_update_paypwd",
+        "line": 122
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "getVCode",
+      "save",
+      "back"
+    ],
+    "navigations": [
+      {
+        "call": "uni.reLaunch",
+        "target": "<dynamic>",
+        "line": 58
+      },
+      {
+        "call": "uni.redirectTo",
+        "target": "<dynamic>",
+        "line": 67
+      }
+    ]
+  },
+  {
+    "path": "pages/member/rnauth",
+    "sourceFile": "pages/member/rnauth.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_rnauth_get",
+        "line": 102
+      },
+      {
+        "method": "post",
+        "action": "user_rnauth_upd",
+        "line": 221
+      },
+      {
+        "method": "post",
+        "action": "user_rnauth_add",
+        "line": 244
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "upimg",
+      "save",
+      "back"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/coin",
+    "sourceFile": "pages/member/coin.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "user_get",
+        "line": 121
+      },
+      {
+        "method": "get",
+        "action": "user_point_list",
+        "line": 146
+      },
+      {
+        "method": "post",
+        "action": "user_coin_recharge",
+        "line": 189
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onBackPress",
+      "loadInfo",
+      "getList",
+      "chargeMoney"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/ghis",
+    "sourceFile": "pages/member/ghis.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 74
+      },
+      {
+        "method": "get",
+        "action": "content_get",
+        "line": 75
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 124
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "onReachBottom",
+      "showRecord",
+      "toLearn",
+      "startLearn",
+      "getWords",
+      "formatTime",
+      "getRecordList"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/his/his_words",
+        "line": 56
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 66
+      }
+    ]
+  },
+  {
+    "path": "pages/member/myke",
+    "sourceFile": "pages/member/myke.vue",
+    "apiCalls": [
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 45
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getCourseList"
+    ],
+    "navigations": []
+  },
+  {
+    "path": "pages/member/ke_xiao",
+    "sourceFile": "pages/member/ke_xiao.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "user_get",
+        "line": 93
+      },
+      {
+        "method": "post",
+        "action": "user_get",
+        "line": 121
+      },
+      {
+        "method": "get",
+        "action": "content_list",
+        "line": 153
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "setStudentUser",
+      "setBaobiao",
+      "getShopList",
+      "onReachBottom",
+      "onPullDownRefresh",
+      "onShow"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 87
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 106
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 115
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "<dynamic>",
+        "line": 134
+      }
+    ]
+  },
+  {
+    "path": "pages/member/ke_fuxi",
+    "sourceFile": "pages/member/ke_fuxi.vue",
+    "apiCalls": [
+      {
+        "method": "post",
+        "action": "e_get_21list_tj",
+        "line": 65
+      },
+      {
+        "method": "post",
+        "action": "e_get_21list",
+        "line": 79
+      },
+      {
+        "method": "post",
+        "action": "content_add",
+        "line": 152
+      }
+    ],
+    "externalCalls": [],
+    "methods": [
+      "data",
+      "onLoad",
+      "getTongji",
+      "getGuyiList",
+      "formatDataList",
+      "toLearn",
+      "onReachBottom"
+    ],
+    "navigations": [
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu7",
+        "line": 155
+      },
+      {
+        "call": "uni.$u.route",
+        "target": "/pages/stu/stu7",
+        "line": 158
+      }
+    ]
+  }
+] as const;
+
+export const SOURCE_API_ACTIONS = [
+  "app_update",
+  "cart_list",
+  "content_add",
+  "content_get",
+  "content_list",
+  "content_list_llk",
+  "content_update",
+  "content_uphis",
+  "coupon_list",
+  "coupon_usrgot_add",
+  "coupon_usrgot_list",
+  "e_add_words",
+  "e_ck_list",
+  "e_get_21list",
+  "e_get_21list_tj",
+  "e_order_detail",
+  "e_order_tongji",
+  "e_order_update_v2",
+  "e_record_detail",
+  "e_user_list",
+  "e_words_list",
+  "guestbook_add",
+  "invoice_add",
+  "invoice_del",
+  "invoice_get",
+  "invoice_list",
+  "invoice_upd",
+  "mcode_send",
+  "node_get",
+  "node_list",
+  "order_comment_add",
+  "order_comment_list",
+  "order_delivery",
+  "order_get",
+  "order_list",
+  "order_signfor",
+  "payment_cart",
+  "payment_cart_again",
+  "payment_success",
+  "product_del",
+  "product_get",
+  "product_list",
+  "product_sale_change",
+  "product_stock_change",
+  "product_stock_list",
+  "pub_add",
+  "pub_list",
+  "receaddr_add",
+  "receaddr_del",
+  "receaddr_get",
+  "receaddr_list",
+  "receaddr_upd",
+  "stu_record_update_v2",
+  "unit_record_update",
+  "user_bank_add",
+  "user_bank_del",
+  "user_bank_get",
+  "user_bank_list",
+  "user_cash_add",
+  "user_cash_list",
+  "user_coin_recharge",
+  "user_dept",
+  "user_exp_transfer",
+  "user_get",
+  "user_group_usr_supply",
+  "user_group_usr_upgrade",
+  "user_info_name",
+  "user_list",
+  "user_login_mobile",
+  "user_login_passwd",
+  "user_money_recharge",
+  "user_money_transfer",
+  "user_point_list",
+  "user_register",
+  "user_register_mobile",
+  "user_rnauth_add",
+  "user_rnauth_get",
+  "user_rnauth_upd",
+  "user_shop_order",
+  "user_shop_sales",
+  "user_star_add",
+  "user_star_del",
+  "user_star_is",
+  "user_sync2",
+  "user_update",
+  "user_update_paypwd",
+  "user_update_pwd",
+  "user_update_pwdall",
+  "vote_add",
+  "vote_ask",
+  "vote_question"
+] as const;
+
+export const SOURCE_EXTERNAL_CALLS = [
+  "bytedesk.addBlock",
+  "bytedesk.agentCloseThread",
+  "bytedesk.getOnlineAgents",
+  "bytedesk.getPrechatSettings",
+  "bytedesk.getQuickButtons",
+  "bytedesk.loadHistoryMessagesByTopic",
+  "bytedesk.markNodisturb",
+  "bytedesk.messageAnswer",
+  "bytedesk.queryAnswer",
+  "bytedesk.queryCategoryAnswers",
+  "bytedesk.rate",
+  "bytedesk.rateAnswer",
+  "bytedesk.rateDetail",
+  "bytedesk.requestAgent",
+  "bytedesk.requestThread",
+  "bytedesk.requestThreadScan",
+  "bytedesk.requestWorkGroupThreadV2",
+  "bytedesk.saveLeaveMessage",
+  "bytedesk.sendMessageRest",
+  "bytedesk.unmarkNodisturb"
+] as const;

+ 34 - 0
src/app/core/source-parity.spec.ts

@@ -0,0 +1,34 @@
+import { PAGE_CATALOG } from './page-catalog';
+import { SOURCE_API_ACTIONS, SOURCE_EXTERNAL_CALLS, SOURCE_PAGE_PARITY } from './source-parity.generated';
+
+describe('uni-app source parity manifest', () => {
+  it('keeps every original page registered exactly once', () => {
+    const sourcePaths = SOURCE_PAGE_PARITY.map((page) => page.path).sort();
+    const angularPaths = PAGE_CATALOG.map((page) => page.path).sort();
+    expect(new Set(angularPaths).size).toBe(angularPaths.length);
+    expect(angularPaths).toEqual(sourcePaths);
+  });
+
+  it('tracks the full source business surface', () => {
+    expect(SOURCE_PAGE_PARITY.length).toBe(95);
+    expect(SOURCE_API_ACTIONS.length).toBe(91);
+    expect(SOURCE_API_ACTIONS).toContain('payment_cart_again');
+    expect(SOURCE_API_ACTIONS).toContain('unit_record_update');
+    expect(SOURCE_API_ACTIONS).toContain('user_group_usr_upgrade');
+  });
+
+  it('keeps dynamic source ranking actions and dedicated rich content rendering', () => {
+    const ranking = PAGE_CATALOG.find((page) => page.path === 'pages/member/ranklist');
+    const article = PAGE_CATALOG.find((page) => page.path === 'pages/home/enjoy_item');
+    expect(ranking?.tabActions).toEqual(['ranklist_1', 'ranklist_2', 'ranklist_3', 'ranklist_4', 'ranklist_5']);
+    expect(article?.kind).toBe('article');
+  });
+
+  it('tracks customer-service calls that bypass uni.$u.http', () => {
+    expect(SOURCE_EXTERNAL_CALLS).toContain('bytedesk.rateDetail');
+    expect(SOURCE_EXTERNAL_CALLS).toContain('bytedesk.rate');
+    expect(SOURCE_EXTERNAL_CALLS).toContain('bytedesk.saveLeaveMessage');
+    expect(PAGE_CATALOG.find((page) => page.path === 'components/bytedesk_kefu/rate')?.kind).toBe('supportFeedback');
+    expect(PAGE_CATALOG.find((page) => page.path === 'components/bytedesk_kefu/leavemsg')?.kind).toBe('supportFeedback');
+  });
+});

+ 65 - 0
src/app/features/appointment/appointment-page.component.html

@@ -0,0 +1,65 @@
+<section class="appointment-page">
+  @if (loading()) {
+    <div class="empty-state"><p>正在加载预约...</p></div>
+  } @else if (error()) {
+    <div class="empty-state error"><div><lucide-icon [img]="icons.CircleHelp" [size]="30" /><p>{{ error() }}</p><button type="button" class="secondary-button" (click)="load()">重新加载</button></div></div>
+  } @else {
+    <header class="appointment-status">
+      <div><span>{{ status() > 20 ? '课程陪练报告' : '预约课程' }}</span><strong>{{ statusText() }}</strong></div>
+      <lucide-icon [img]="status() >= 30 ? icons.Flag : icons.Clock3" [size]="28" />
+    </header>
+
+    <dl class="appointment-info">
+      <div><dt>学生姓名</dt><dd>{{ field('student') || '-' }}</dd></div>
+      <div><dt>预约课程</dt><dd>{{ field('course') || '-' }}</dd></div>
+      <div><dt>课程类型</dt><dd>{{ field('type') || '-' }}</dd></div>
+      <div><dt>预约时间</dt><dd>{{ field('date') }} {{ field('time') }}@if (field('end')) { ~ {{ field('end') }} }</dd></div>
+      <div><dt>交付方式</dt><dd>{{ field('delivery') || '-' }}</dd></div>
+      @if (status() > 20) { <div><dt>陪练老师</dt><dd>{{ field('coach') || '-' }}</dd></div><div><dt>学习成果</dt><dd>{{ order()['dsxueshi'] || 0 }} 学时,累计识词 {{ order()['learned'] || 0 }}</dd></div> }
+    </dl>
+
+    @if (!isStudentView && status() <= 10 && !isWriting) {
+      <section class="course-section">
+        <label><span>本次课程</span><select [(ngModel)]="selectedCourse" (change)="chooseCourse()"><option value="">请选择课程</option>@for (course of courses(); track course['kcid'] ?? $index) { <option [value]="course['kcid']">{{ course['Title'] }}</option> }</select></label>
+        <div class="unit-heading"><strong>选择学习单元</strong><span>可多选,长按标记功能在 Web 中改为右侧按钮</span></div>
+        <div class="unit-grid">
+          @for (unit of units(); track unit['NodeID'] ?? $index) {
+            <article [class.selected]="selectedUnits().has(unitId(unit))" [class.completed]="completedUnits().has(unitId(unit))">
+              <button type="button" class="unit-select" (click)="toggleUnit(unit)"><span class="check">@if (selectedUnits().has(unitId(unit))) { <lucide-icon [img]="icons.Check" [size]="16" /> }</span><strong>{{ unitTitle(unit) }}</strong></button>
+              <button type="button" class="mark-complete" title="标记已学完" aria-label="标记已学完" (click)="markUnitComplete(unit)"><lucide-icon [img]="icons.BookCheck" [size]="17" /></button>
+            </article>
+          } @empty { <div class="empty-state"><p>请选择课程后加载单元</p></div> }
+        </div>
+      </section>
+    }
+
+    @if (!isStudentView && (status() === 11 || status() === 20)) {
+      <section class="comment-panel"><label><span>陪练点评</span><textarea [(ngModel)]="coachComment" maxlength="200" placeholder="请输入本次陪练点评"></textarea></label></section>
+    }
+
+    @if (isStudentView && status() === 11) {
+      <section class="comment-panel">
+        <label><span>本次评分</span><select [(ngModel)]="studentScore">@for (score of [5,4,3,2,1]; track score) { <option [ngValue]="score">{{ score }} 分</option> }</select></label>
+        <label><span>学习评价</span><textarea [(ngModel)]="studentComment" maxlength="200" placeholder="请输入评价内容"></textarea></label>
+      </section>
+    }
+
+    @if (status() > 20 || practiceRecord()['pldp'] || practiceRecord()['pjnr']) {
+      <section class="report-panel">
+        <h2><lucide-icon [img]="icons.Star" [size]="20" />课程评价</h2>
+        <div><span>学员评分</span><strong>{{ practiceRecord()['pf'] || '-' }}@if (practiceRecord()['pf']) { 分 }</strong></div>
+        <p>{{ practiceRecord()['pjnr'] || '学员暂无文字评价' }}</p>
+        <div><span>陪练点评</span><strong>{{ practiceRecord()['plpjsj'] || '' }}</strong></div>
+        <p>{{ practiceRecord()['pldp'] || '陪练暂无文字点评' }}</p>
+      </section>
+    }
+
+    <div class="appointment-actions">
+      @if (!isStudentView && status() === 0) { <button type="button" class="primary-button" [disabled]="submitting()" (click)="startOrContinue()"><lucide-icon [img]="icons.Play" [size]="18" />开始课程</button> }
+      @if (!isStudentView && status() === 10) { <button type="button" class="secondary-button" [disabled]="submitting()" (click)="startOrContinue()">继续课程<lucide-icon [img]="icons.ChevronRight" [size]="18" /></button><button type="button" class="primary-button" [disabled]="submitting()" (click)="finishCoachLesson()">结束学习</button> }
+      @if (!isStudentView && (status() === 11 || status() === 20)) { <button type="button" class="primary-button" [disabled]="submitting()" (click)="completeCoachOrder()"><lucide-icon [img]="icons.Save" [size]="18" />生成学习报告</button> }
+      @if (isStudentView && status() === 11) { <button type="button" class="primary-button" [disabled]="submitting()" (click)="finishStudentLesson()"><lucide-icon [img]="icons.GraduationCap" [size]="18" />确认学习结束</button> }
+      @if (isStudentView && status() === 20) { <button type="button" class="secondary-button" disabled>已确认结束</button> }
+    </div>
+  }
+</section>

+ 34 - 0
src/app/features/appointment/appointment-page.component.scss

@@ -0,0 +1,34 @@
+.appointment-page { min-height: 100%; padding-bottom: 78px; }
+.appointment-status { display: flex; align-items: center; justify-content: space-between; padding: 20px 17px; color: white; background: var(--brand-strong); }
+.appointment-status > div { display: flex; flex-direction: column; gap: 5px; }
+.appointment-status span { font-size: 12px; opacity: .82; }
+.appointment-status strong { font-size: 20px; }
+.appointment-info { margin: 0; padding: 5px 16px; background: white; }
+.appointment-info div { display: grid; min-height: 46px; grid-template-columns: 92px minmax(0, 1fr); align-items: center; border-bottom: 1px solid var(--line); gap: 10px; }
+.appointment-info div:last-child { border-bottom: 0; }
+.appointment-info dt { color: var(--ink-soft); font-size: 13px; }
+.appointment-info dd { margin: 0; text-align: right; overflow-wrap: anywhere; }
+.course-section, .comment-panel, .report-panel { margin-top: 10px; padding: 15px; background: white; border-block: 1px solid var(--line); }
+.course-section > label, .comment-panel label { display: grid; gap: 7px; }
+.course-section label span, .comment-panel label span { color: var(--ink-soft); font-size: 12px; }
+.course-section select, .comment-panel select, .comment-panel textarea { width: 100%; min-height: 45px; padding: 9px 11px; color: var(--ink); background: white; border: 1px solid var(--line); border-radius: var(--radius-sm); }
+.comment-panel label + label { margin-top: 14px; }
+.comment-panel textarea { min-height: 110px; resize: vertical; }
+.unit-heading { display: flex; align-items: flex-end; justify-content: space-between; margin: 19px 0 10px; gap: 12px; }
+.unit-heading span { color: var(--ink-soft); font-size: 10px; text-align: right; }
+.unit-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
+.unit-grid article { position: relative; min-height: 62px; overflow: hidden; background: white; border: 1px solid var(--line); border-radius: var(--radius-md); }
+.unit-grid article.selected { border-color: var(--brand-strong); }
+.unit-grid article.completed { background: #fff7e7; border-color: #dfb85c; }
+.unit-select { display: flex; width: 100%; min-height: 62px; align-items: center; padding: 9px 38px 9px 9px; color: var(--ink); text-align: left; background: transparent; border: 0; gap: 8px; }
+.unit-select .check { display: grid; width: 19px; height: 19px; flex: 0 0 19px; color: white; border: 1px solid var(--line); border-radius: 4px; place-items: center; }
+.selected .unit-select .check { background: var(--brand-strong); border-color: var(--brand-strong); }
+.unit-select strong { font-size: 13px; overflow-wrap: anywhere; }
+.mark-complete { position: absolute; top: 0; right: 0; display: grid; width: 34px; height: 100%; padding: 0; color: #8a5b12; background: #fff3d9; border: 0; place-items: center; }
+.report-panel h2 { display: flex; align-items: center; margin: 0 0 14px; font-size: 17px; gap: 7px; }
+.report-panel div { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; color: var(--ink-soft); font-size: 12px; }
+.report-panel p { margin: 7px 0 14px; padding: 11px; color: var(--ink); line-height: 1.6; background: #f6f8f7; border-radius: var(--radius-sm); }
+.appointment-actions { position: fixed; z-index: 20; right: 0; bottom: 0; left: 0; display: flex; padding: 10px 14px max(10px, env(safe-area-inset-bottom)); background: rgb(255 255 255 / 96%); border-top: 1px solid var(--line); gap: 9px; }
+.appointment-actions button { display: flex; min-height: 49px; align-items: center; justify-content: center; flex: 1; gap: 6px; }
+.empty-state.error > div { display: grid; justify-items: center; gap: 9px; }
+@media (min-width: 760px) { .appointment-page { max-width: 760px; margin: 0 auto; border-inline: 1px solid var(--line); } .appointment-actions { right: calc((100vw - 760px) / 2); left: calc((100vw - 760px) / 2); } }

+ 384 - 0
src/app/features/appointment/appointment-page.component.ts

@@ -0,0 +1,384 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { BookCheck, Check, ChevronRight, CircleHelp, Clock3, Flag, GraduationCap, LucideAngularModule, Play, Save, Star, X } from 'lucide-angular';
+import { finalize } from 'rxjs';
+import { ApiError, ApiService } from '../../core/api.service';
+import { PageMeta } from '../../core/models';
+import { PlatformService } from '../../core/platform.service';
+import { SessionService } from '../../core/session.service';
+
+type SourceItem = Record<string, unknown>;
+
+@Component({
+  selector: 'app-appointment-page',
+  standalone: true,
+  imports: [CommonModule, FormsModule, LucideAngularModule],
+  templateUrl: './appointment-page.component.html',
+  styleUrl: './appointment-page.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppointmentPageComponent implements OnInit {
+  readonly meta = inject(ActivatedRoute).snapshot.data['meta'] as PageMeta;
+  readonly order = signal<SourceItem>({});
+  readonly practiceRecord = signal<SourceItem>({});
+  readonly learningRecord = signal<SourceItem>({});
+  readonly courses = signal<SourceItem[]>([]);
+  readonly units = signal<SourceItem[]>([]);
+  readonly selectedUnits = signal<Set<string>>(new Set());
+  readonly completedUnits = signal<Set<string>>(new Set());
+  readonly bindingRecord = signal<SourceItem>({});
+  readonly loading = signal(true);
+  readonly submitting = signal(false);
+  readonly error = signal('');
+  readonly icons = { BookCheck, Check, ChevronRight, CircleHelp, Clock3, Flag, GraduationCap, Play, Save, Star, X };
+  readonly status = computed(() => Number(this.order()['dszt'] ?? this.order()['status'] ?? 0));
+
+  selectedCourse = '';
+  coachComment = '';
+  studentScore = 5;
+  studentComment = '';
+
+  private readonly route = inject(ActivatedRoute);
+  private readonly router = inject(Router);
+  private readonly api = inject(ApiService);
+  private readonly session = inject(SessionService);
+  private readonly platform = inject(PlatformService);
+
+  ngOnInit(): void {
+    this.load();
+  }
+
+  get isStudentView(): boolean { return this.meta.path === 'pages/shop/stu_get_order'; }
+  get orderId(): string { return this.route.snapshot.queryParamMap.get('id') ?? this.route.snapshot.queryParamMap.get('cart') ?? ''; }
+  get isWriting(): boolean { return String(this.order()['if_writing'] ?? '') === '1'; }
+
+  load(): void {
+    if (!this.orderId) {
+      this.loading.set(false);
+      this.error.set('缺少预约编号');
+      return;
+    }
+    this.loading.set(true);
+    this.error.set('');
+    this.api.get<unknown>('e_order_detail', { id: this.orderId }).pipe(finalize(() => this.loading.set(false))).subscribe({
+      next: ({ result }) => {
+        const order = this.findList(result)[0] ?? this.asRecord(result);
+        this.order.set(order);
+        this.selectedCourse = String(order['kcid'] ?? '');
+        this.coachComment = String(order['pldp'] ?? '');
+        this.loadPracticeRecord();
+        if (!this.isStudentView) {
+          this.loadCourses();
+          this.loadLearningRecord();
+          if (this.status() <= 10 && this.selectedCourse) this.loadUnits();
+        }
+      },
+      error: (error: ApiError) => this.error.set(error.message),
+    });
+  }
+
+  field(label: string): string {
+    const aliases: Record<string, string[]> = {
+      student: ['HoneyName', 'honeyName', 'xymz_t'],
+      course: ['yykcmc', 'kcmc', 'Title'],
+      type: ['dslx', 'dslx_t', 'kclx'],
+      date: ['bxrq', 'yydate'],
+      time: ['sdsd', 'startTime'],
+      end: ['jieshu', 'endTime'],
+      delivery: ['jffs'],
+      coach: ['plxm', 'plName'],
+    };
+    for (const key of aliases[label] ?? []) if (this.order()[key] !== undefined) return String(this.order()[key]);
+    return '';
+  }
+
+  statusText(): string {
+    return String(this.order()['status_txt'] ?? ({ 0: '待开始', 10: '学习中', 11: '等待学员确认', 20: '学员已确认', 30: '已完成' } as Record<number, string>)[this.status()] ?? `状态 ${this.status()}`);
+  }
+
+  chooseCourse(): void {
+    const selected = this.courses().find((course) => String(course['kcid'] ?? course['kcId']) === this.selectedCourse);
+    if (!selected) return;
+    this.order.update((order) => ({ ...order, kcid: this.selectedCourse, kcmc: selected['Title'] }));
+    const content = { GeneralID: this.orderId, UpDateTime: this.dateTime() };
+    this.api.post('content_update', { content: JSON.stringify(content), addon: JSON.stringify({ kcid: this.selectedCourse }) }).subscribe({
+      next: () => this.loadUnits(),
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  toggleUnit(unit: SourceItem): void {
+    const id = String(unit['NodeID'] ?? unit['ID'] ?? '');
+    this.selectedUnits.update((current) => {
+      const next = new Set(current);
+      if (next.has(id)) next.delete(id); else next.add(id);
+      return next;
+    });
+  }
+
+  async markUnitComplete(unit: SourceItem): Promise<void> {
+    if (!(await this.platform.confirm(`确认将“${this.unitTitle(unit)}”标记为已学完?`))) return;
+    const id = String(unit['NodeID'] ?? unit['ID'] ?? '');
+    const binding = this.bindingRecord();
+    if (!binding['GeneralID']) {
+      this.platform.toast('未找到课程进度记录', 'error');
+      return;
+    }
+    const progress = this.parseRecord(binding['syjd']);
+    progress[id] = `${id}@${this.unitTitle(unit)}@1`;
+    this.api.post('content_update', {
+      content: JSON.stringify({ GeneralID: binding['GeneralID'], ModelID: 58 }),
+      addon: JSON.stringify({ syjd: JSON.stringify(progress) }),
+    }).subscribe({
+      next: () => this.completedUnits.update((current) => new Set(current).add(id)),
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  unitTitle(unit: SourceItem): string {
+    return String(unit['NodeName'] ?? unit['Title'] ?? '学习单元');
+  }
+
+  unitId(unit: SourceItem): string {
+    return String(unit['NodeID'] ?? unit['ID'] ?? '');
+  }
+
+  startOrContinue(): void {
+    if (!this.isWriting && !this.selectedUnits().size) {
+      this.platform.toast('请选择单元', 'error');
+      return;
+    }
+    this.updateOrderStatus(10, () => {
+      if (this.isWriting) {
+        this.load();
+        return;
+      }
+      void this.router.navigate(['/pages/stu/stu_abc'], {
+        queryParams: { nids: [...this.selectedUnits()].join(','), pnid: this.selectedCourse, orderId: this.orderId },
+      });
+    });
+  }
+
+  async finishCoachLesson(): Promise<void> {
+    if (!(await this.platform.confirm('即将结束本次学习,确认继续?'))) return;
+    if (this.isWriting) {
+      this.saveWritingLearningRecord(() => this.finishCoachStatus());
+    } else {
+      this.finishCoachStatus();
+    }
+  }
+
+  completeCoachOrder(): void {
+    if (!this.coachComment.trim()) {
+      this.platform.toast('请填写陪练点评', 'error');
+      return;
+    }
+    this.updateOrderStatus(30, () => this.saveCoachComment());
+  }
+
+  async finishStudentLesson(): Promise<void> {
+    if (!(await this.platform.confirm('即将确认本次学习结束,确认继续?'))) return;
+    if (this.studentScore < 1 || this.studentScore > 5) {
+      this.platform.toast('评分应为 1 到 5 分', 'error');
+      return;
+    }
+    this.updateOrderStatus(20, () => this.saveStudentComment());
+  }
+
+  private finishCoachStatus(): void {
+    this.updateOrderStatus(11, () => this.savePracticeSession());
+  }
+
+  private updateOrderStatus(status: number, next: () => void): void {
+    this.submitting.set(true);
+    this.api.post('e_order_update_v2', {
+      status,
+      uid: this.session.user()?.userId,
+      content: JSON.stringify({ GeneralID: this.orderId, UpDateTime: this.dateTime() }),
+    }).pipe(finalize(() => this.submitting.set(false))).subscribe({
+      next: () => next(),
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  private saveCoachComment(): void {
+    const record = this.practiceRecord();
+    if (!record['GeneralID']) {
+      this.platform.toast('未找到陪练记录', 'error');
+      return;
+    }
+    const now = this.dateTime();
+    this.api.post('content_update', {
+      content: JSON.stringify({ GeneralID: record['GeneralID'], UpDateTime: now }),
+      addon: JSON.stringify({ pldp: this.coachComment, plpjsj: now, kclx: this.order()['dslx_t'] }),
+    }).subscribe({
+      next: () => this.load(),
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  private saveStudentComment(): void {
+    const record = this.practiceRecord();
+    if (!record['GeneralID']) {
+      this.platform.toast('未找到陪练记录', 'error');
+      return;
+    }
+    this.api.post('content_update', {
+      content: JSON.stringify({ GeneralID: record['GeneralID'], UpDateTime: this.dateTime() }),
+      addon: JSON.stringify({ pf: this.studentScore, pjnr: this.studentComment }),
+    }).subscribe({
+      next: () => this.load(),
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  private savePracticeSession(): void {
+    const student = this.session.student();
+    const user = this.session.user();
+    const existing = this.practiceRecord();
+    const content: SourceItem = {
+      ModelID: 59,
+      nodeId: 296,
+      inputer: user?.honeyName,
+      status: 99,
+      Hits: 0,
+      title: student?.honeyName ?? this.field('student'),
+    };
+    let action = 'content_add';
+    if (existing['GeneralID']) {
+      content['GeneralID'] = existing['GeneralID'];
+      content['UpDateTime'] = this.dateOnly();
+      action = 'content_update';
+    }
+    const addon = {
+      jsmz: user?.userId,
+      kcid: this.selectedCourse,
+      xymz: student?.userId ?? this.order()['yhid'],
+      kclx: this.order()['dslx_t'],
+      yyds: this.orderId,
+      jffs: this.order()['jffs'],
+      kcmc: this.order()['kcmc'],
+      szmdid: this.order()['szmd'],
+      kzsj: existing['kzsj'] ?? '[]',
+    };
+    this.api.post(action, { content: JSON.stringify(content), addon: JSON.stringify(addon) }).subscribe({
+      next: () => this.load(),
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  private saveWritingLearningRecord(next: () => void): void {
+    const user = this.session.user();
+    const student = Number(user?.groupId) === 3 ? this.session.student() : null;
+    const owner = student ?? user;
+    const existing = this.learningRecord();
+    const content: SourceItem = { ModelID: 56, nodeId: 291, inputer: user?.honeyName, status: 99, Hits: 0, title: owner?.honeyName };
+    let action = 'content_add_zt';
+    if (existing['GeneralID']) {
+      content['GeneralID'] = existing['GeneralID'];
+      action = 'stu_record_update';
+    }
+    const addon = {
+      con: `完成了《${String(this.order()['kcmc'] ?? '')}》的学习@${this.dateTime()}`,
+      szmdid: this.order()['szmd'],
+      dqrq: this.dateOnly().replaceAll('-', ''),
+      dsid: this.orderId,
+      pl: student ? user?.userId : 0,
+      UserID: owner?.userId,
+      learned: 0,
+      ygg: 0,
+      djq: 0,
+      xxqs: '',
+    };
+    this.api.post(action, { content: JSON.stringify(content), addon: JSON.stringify(addon) }).subscribe({
+      next: () => next(),
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  private loadPracticeRecord(): void {
+    const user = this.session.user();
+    const targetField = this.isStudentView ? `yyds=${this.orderId}|xymz=${user?.userId}` : `yyds=${this.orderId}|jsmz=${user?.userId}`;
+    this.api.get<unknown>('content_list', { ModelID: 59, psize: 2, myfield2: targetField }).subscribe({
+      next: ({ result }) => {
+        const record = this.findList(result)[0] ?? {};
+        this.practiceRecord.set(record);
+        this.coachComment = String(record['pldp'] ?? this.coachComment);
+        this.studentScore = Number(record['pf'] ?? this.studentScore);
+        this.studentComment = String(record['pjnr'] ?? '');
+      },
+      error: () => undefined,
+    });
+  }
+
+  private loadCourses(): void {
+    const student = this.session.student();
+    if (!student?.userId) return;
+    this.api.get<unknown>('content_list', { inputer: '', nid: 28, myfield: `yhid=${student.userId}`, orders: 'id=DESC', psize: 200 }).subscribe({
+      next: ({ result }) => this.courses.set(this.findList(result).map((item) => {
+        const parts = String(item['Title'] ?? '').split('|');
+        return { ...item, Title: parts[parts.length - 1] || '-', kcId: item['kcid'] };
+      })),
+      error: () => undefined,
+    });
+  }
+
+  private loadUnits(): void {
+    this.api.get<unknown>('node_list', { pid: this.selectedCourse, ifunit: 1, userId: this.session.user()?.userId }).subscribe({
+      next: ({ result }) => {
+        this.units.set(this.findList(result));
+        this.loadUnitProgress();
+      },
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  private loadUnitProgress(): void {
+    const owner = Number(this.session.user()?.groupId) === 3 ? this.session.student() : this.session.user();
+    this.api.post<unknown>('content_list', { ModelID: 58, myfield2: `yhid=${owner?.userId}|kcid=${this.selectedCourse}` }).subscribe({
+      next: ({ result }) => {
+        const record = this.findList(result)[0] ?? {};
+        this.bindingRecord.set(record);
+        this.completedUnits.set(new Set(Object.keys(this.parseRecord(record['syjd']))));
+      },
+      error: () => undefined,
+    });
+  }
+
+  private loadLearningRecord(): void {
+    const student = this.session.student();
+    if (!student?.userId) return;
+    this.api.get<unknown>('content_list', { ModelID: 56, psize: 1, myfield: `dsid=${this.orderId}`, myfield2: `UserId=${student.userId}` }).subscribe({
+      next: ({ result }) => this.learningRecord.set(this.findList(result)[0] ?? {}),
+      error: () => undefined,
+    });
+  }
+
+  private parseRecord(value: unknown): Record<string, string> {
+    if (value && typeof value === 'object' && !Array.isArray(value)) return value as Record<string, string>;
+    if (typeof value !== 'string' || !value) return {};
+    try { return JSON.parse(value) as Record<string, string>; } catch { return {}; }
+  }
+
+  private findList(value: unknown): SourceItem[] {
+    if (Array.isArray(value)) return value as SourceItem[];
+    if (value && typeof value === 'object') for (const child of Object.values(value)) if (Array.isArray(child)) return child as SourceItem[];
+    return [];
+  }
+
+  private asRecord(value: unknown): SourceItem {
+    return value && typeof value === 'object' && !Array.isArray(value) ? value as SourceItem : {};
+  }
+
+  private dateOnly(): string {
+    const now = new Date();
+    return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
+  }
+
+  private dateTime(): string {
+    const now = new Date();
+    return `${this.dateOnly()} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
+  }
+}

+ 8 - 0
src/app/features/article/article-page.component.html

@@ -0,0 +1,8 @@
+@if (loading()) { <div class="article-state"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="22" />正在加载内容</div> }
+@else if (article()) {
+  <article class="article-page">
+    <header><h1>{{ title() }}</h1>@if (publishedAt()) { <p><lucide-icon [img]="icons.Clock3" [size]="15" />发布于:{{ publishedAt() }}</p> }</header>
+    <section class="article-content" [innerHTML]="content()"></section>
+    @if (videoUrl()) { <video class="article-video" [src]="videoUrl()" controls playsinline></video> }
+  </article>
+}

+ 14 - 0
src/app/features/article/article-page.component.scss

@@ -0,0 +1,14 @@
+.article-page { padding-bottom: 24px; }
+.article-page header, .article-content { margin-bottom: 10px; padding: 18px 16px; background: white; }
+.article-page h1 { margin: 0 0 12px; font-size: 23px; line-height: 1.35; text-align: center; letter-spacing: 0; }
+.article-page header p { display: flex; align-items: center; justify-content: center; margin: 0; color: var(--ink-soft); font-size: 12px; gap: 6px; }
+.article-content { min-height: 180px; color: var(--ink); font-size: 15px; line-height: 1.8; overflow-wrap: anywhere; }
+.article-content :is(p, ul, ol, blockquote) { margin: 0 0 12px; }
+.article-content :is(img, table) { max-width: 100%; }
+.article-content img { height: auto; border-radius: var(--radius-sm); }
+.article-content table { display: block; overflow-x: auto; border-collapse: collapse; }
+.article-video { display: block; width: 100%; max-height: 460px; background: #111; }
+.article-state { display: flex; min-height: 260px; align-items: center; justify-content: center; color: var(--ink-soft); gap: 8px; }
+.spin { animation: spin .8s linear infinite; }
+@keyframes spin { to { transform: rotate(360deg); } }
+@media (min-width: 760px) { .article-page { max-width: 760px; margin: 0 auto; border-inline: 1px solid var(--line); } }

+ 78 - 0
src/app/features/article/article-page.component.ts

@@ -0,0 +1,78 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, inject, OnDestroy, OnInit, signal } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Clock3, LoaderCircle, LucideAngularModule } from 'lucide-angular';
+import { finalize } from 'rxjs';
+import { ApiError, ApiService } from '../../core/api.service';
+import { PlatformService } from '../../core/platform.service';
+
+type Article = Record<string, unknown>;
+
+@Component({
+  selector: 'app-article-page',
+  standalone: true,
+  imports: [CommonModule, LucideAngularModule],
+  templateUrl: './article-page.component.html',
+  styleUrl: './article-page.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ArticlePageComponent implements OnInit, OnDestroy {
+  readonly article = signal<Article | null>(null);
+  readonly content = signal('');
+  readonly loading = signal(false);
+  readonly icons = { Clock3, LoaderCircle };
+
+  private readonly route = inject(ActivatedRoute);
+  private readonly api = inject(ApiService);
+  private readonly platform = inject(PlatformService);
+  private hitsTimer?: ReturnType<typeof setTimeout>;
+
+  ngOnInit(): void {
+    const id = this.route.snapshot.queryParamMap.get('id');
+    if (!id) { this.platform.toast('缺少内容编号', 'error'); return; }
+    this.loading.set(true);
+    this.api.get<unknown>('content_get', { id }).pipe(finalize(() => this.loading.set(false))).subscribe({
+      next: ({ result }) => {
+        const article = Array.isArray(result) ? this.asRecord(result[0]) : this.asRecord(result);
+        this.article.set(article);
+        this.content.set(this.normalizeContent(article['content'] ?? article['Content']));
+        const generalId = article['generalId'] ?? article['GeneralID'] ?? id;
+        this.hitsTimer = setTimeout(() => this.api.post('content_uphis', { id: generalId }).subscribe({ error: () => undefined }), 15_000);
+      },
+      error: (error: ApiError) => this.platform.toast(`内容加载失败:${error.message}`, 'error'),
+    });
+  }
+
+  ngOnDestroy(): void { if (this.hitsTimer) clearTimeout(this.hitsTimer); }
+
+  title(): string { return String(this.article()?.['title'] ?? this.article()?.['Title'] ?? '内容详情'); }
+  publishedAt(): string {
+    const value = this.article()?.['createTime'] ?? this.article()?.['CreateTime'] ?? this.article()?.['AddTime'];
+    if (!value) return '';
+    const date = new Date(String(value));
+    if (Number.isNaN(date.getTime())) return String(value);
+    return new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false }).format(date).replaceAll('/', '-');
+  }
+  videoUrl(): string {
+    const value = String(this.article()?.['mp4'] ?? this.article()?.['MP4'] ?? '').trim();
+    if (!value) return '';
+    return /^https?:\/\//i.test(value) ? value : `https://a021.2018.z01.com/${value.replace(/^\//, '')}`;
+  }
+
+  private normalizeContent(value: unknown): string {
+    const html = String(value ?? '');
+    if (typeof DOMParser === 'undefined') return html;
+    const document = new DOMParser().parseFromString(html, 'text/html');
+    for (const element of Array.from(document.querySelectorAll<HTMLElement>('[src]'))) {
+      const path = element.getAttribute('src');
+      if (path) element.setAttribute('src', this.api.assetUrl(path));
+    }
+    for (const element of Array.from(document.querySelectorAll<HTMLAnchorElement>('a[href]'))) {
+      element.setAttribute('target', '_blank');
+      element.setAttribute('rel', 'noopener');
+    }
+    return document.body.innerHTML;
+  }
+
+  private asRecord(value: unknown): Article { return value && typeof value === 'object' && !Array.isArray(value) ? value as Article : {}; }
+}

+ 30 - 0
src/app/features/auth/login-page.component.html

@@ -0,0 +1,30 @@
+<main class="login-page">
+  <section class="login-brand">
+    <div class="logo-mark"><lucide-icon [img]="icons.Leaf" [size]="30" /></div>
+    <div><h1>小树陪练</h1><p>登录后继续你的学习计划</p></div>
+  </section>
+
+  <div class="mode-tabs" role="tablist">
+    <button type="button" role="tab" [class.active]="mode() === 'mobile'" (click)="switchMode('mobile')">短信登录</button>
+    <button type="button" role="tab" [class.active]="mode() === 'classic'" (click)="switchMode('classic')">账号密码</button>
+  </div>
+
+  <form (ngSubmit)="submit()">
+    @if (auditMessage()) { <div class="audit-notice" role="status">{{ auditMessage() }}</div> }
+    @if (mode() === 'mobile') {
+      <div class="input-wrap"><lucide-icon [img]="icons.Phone" [size]="19" /><input name="mobile" type="tel" inputmode="numeric" autocomplete="tel" maxlength="11" [(ngModel)]="mobile" placeholder="11位手机号码" /></div>
+      <div class="input-wrap code"><lucide-icon [img]="icons.MessageSquareText" [size]="19" /><input name="code" inputmode="numeric" maxlength="6" [(ngModel)]="code" placeholder="6位短信验证码" /><button type="button" (click)="sendCode()" [disabled]="countdown() > 0">{{ countdown() ? countdown() + '秒' : '获取验证码' }}</button></div>
+    } @else {
+      <div class="input-wrap"><lucide-icon [img]="icons.Phone" [size]="19" /><input name="account" autocomplete="username" [(ngModel)]="account" placeholder="手机号码或账号" /></div>
+      <div class="input-wrap"><lucide-icon [img]="icons.LockKeyhole" [size]="19" /><input name="password" [type]="showPassword() ? 'text' : 'password'" autocomplete="current-password" [(ngModel)]="password" placeholder="6至18位密码" /><button class="eye-button" type="button" aria-label="显示或隐藏密码" title="显示或隐藏密码" (click)="showPassword.set(!showPassword())"><lucide-icon [img]="showPassword() ? icons.EyeOff : icons.Eye" [size]="18" /></button></div>
+      @if (registering()) { <div class="input-wrap"><lucide-icon [img]="icons.LockKeyhole" [size]="19" /><input name="confirmPassword" type="password" autocomplete="new-password" [(ngModel)]="confirmPassword" placeholder="再次输入密码" /></div> }
+    }
+
+    @if (registering()) { <div class="input-wrap"><lucide-icon [img]="icons.UserPlus" [size]="19" /><input name="inviteCode" [(ngModel)]="inviteCode" placeholder="邀请码(选填)" /></div> }
+
+    <label class="privacy"><input name="privacy" type="checkbox" [(ngModel)]="privacyChecked" /><span>我已阅读并同意 <a routerLink="/pages/home/enjoy_item" [queryParams]="{ id: 120243 }">服务协议</a> 和 <a routerLink="/pages/home/enjoy_item" [queryParams]="{ id: 120242 }">隐私政策</a></span></label>
+
+    <button class="primary-button submit" type="submit">{{ registering() ? '注册并登录' : '登录' }}</button>
+    <button class="switch-register" type="button" (click)="registering.set(!registering())">{{ registering() ? '已有账号,返回登录' : '没有账号,立即注册' }}</button>
+  </form>
+</main>

+ 21 - 0
src/app/features/auth/login-page.component.scss

@@ -0,0 +1,21 @@
+:host { display: block; min-height: 100dvh; background: linear-gradient(180deg, var(--brand-pale) 0, #f6f8f7 42%, #fff 100%); }
+.login-page { width: min(100%, 480px); margin: 0 auto; padding: max(48px, env(safe-area-inset-top)) 24px 36px; }
+.login-brand { display: flex; align-items: center; margin-bottom: 34px; gap: 14px; }
+.logo-mark { display: grid; width: 54px; height: 54px; color: white; background: var(--brand-strong); border-radius: var(--radius-md); place-items: center; box-shadow: 0 9px 22px rgb(39 125 105 / 24%); }
+.login-brand h1 { margin: 0; font-size: 27px; letter-spacing: 0; }
+.login-brand p { margin: 4px 0 0; color: var(--ink-soft); }
+.mode-tabs { display: grid; grid-template-columns: repeat(2, 1fr); margin-bottom: 22px; border-bottom: 1px solid var(--line); }
+.mode-tabs button { min-height: 44px; color: var(--ink-soft); background: transparent; border: 0; border-bottom: 2px solid transparent; }
+.mode-tabs button.active { color: var(--brand-strong); border-bottom-color: var(--brand-strong); font-weight: 650; }
+form { display: grid; gap: 14px; }
+.input-wrap { display: grid; min-height: 50px; grid-template-columns: 24px minmax(0, 1fr) auto; align-items: center; padding: 0 13px; color: var(--ink-soft); background: white; border: 1px solid var(--line); border-radius: var(--radius-md); gap: 9px; }
+.input-wrap:focus-within { border-color: var(--brand-strong); box-shadow: 0 0 0 3px rgb(39 125 105 / 12%); }
+.input-wrap input { width: 100%; height: 48px; padding: 0; color: var(--ink); background: transparent; border: 0; outline: 0; }
+.input-wrap button { height: 34px; padding: 0 0 0 12px; color: var(--brand-strong); background: transparent; border: 0; border-left: 1px solid var(--line); }
+.input-wrap .eye-button { display: grid; width: 34px; padding: 0; border-left: 0; place-items: center; }
+.privacy { display: grid; grid-template-columns: 20px minmax(0, 1fr); align-items: start; margin-top: 3px; color: var(--ink-soft); font-size: 13px; line-height: 1.55; gap: 8px; }
+.privacy input { width: 17px; height: 17px; margin: 2px 0 0; accent-color: var(--brand-strong); }
+.privacy a { color: var(--brand-strong); }
+.submit { width: 100%; margin-top: 5px; }
+.switch-register { min-height: 42px; color: var(--brand-strong); background: transparent; border: 0; }
+.audit-notice { margin-bottom: 12px; padding: 10px 12px; color: #8a5a00; background: #fff6dc; border: 1px solid #f1d38a; border-radius: var(--radius-sm); font-size: 13px; }

+ 140 - 0
src/app/features/auth/login-page.component.ts

@@ -0,0 +1,140 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, inject, OnDestroy, signal } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ActivatedRoute, Router, RouterLink } from '@angular/router';
+import { Eye, EyeOff, Leaf, LockKeyhole, LucideAngularModule, MessageSquareText, Phone, UserPlus } from 'lucide-angular';
+import { finalize } from 'rxjs';
+import { ApiError, ApiService } from '../../core/api.service';
+import { AuthService } from '../../core/auth.service';
+import { AppUser } from '../../core/models';
+import { PlatformService } from '../../core/platform.service';
+import { SessionService } from '../../core/session.service';
+
+@Component({
+  selector: 'app-login-page',
+  standalone: true,
+  imports: [CommonModule, FormsModule, LucideAngularModule, RouterLink],
+  templateUrl: './login-page.component.html',
+  styleUrl: './login-page.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class LoginPageComponent implements OnDestroy {
+  readonly mode = signal<'mobile' | 'classic'>('mobile');
+  readonly registering = signal(false);
+  readonly showPassword = signal(false);
+  readonly countdown = signal(0);
+  readonly auditMessage = signal('');
+  readonly icons = { Eye, EyeOff, Leaf, LockKeyhole, MessageSquareText, Phone, UserPlus };
+
+  mobile = '';
+  code = '';
+  account = '';
+  password = '';
+  confirmPassword = '';
+  inviteCode = '';
+  privacyChecked = false;
+  private timer: ReturnType<typeof setInterval> | null = null;
+
+  private readonly api = inject(ApiService);
+  private readonly auth = inject(AuthService);
+  private readonly session = inject(SessionService);
+  private readonly platform = inject(PlatformService);
+  private readonly router = inject(Router);
+  private readonly route = inject(ActivatedRoute);
+
+  ngOnDestroy(): void { if (this.timer) clearInterval(this.timer); }
+
+  switchMode(mode: 'mobile' | 'classic'): void {
+    this.mode.set(mode);
+    this.registering.set(false);
+  }
+
+  sendCode(): void {
+    if (!this.validMobile(this.mobile)) {
+      this.platform.toast('请输入正确的11位手机号码', 'error');
+      return;
+    }
+    if (this.countdown()) return;
+    this.platform.loading.set(true);
+    this.api.post('mcode_send', { mobile: this.mobile, va: 1, source: this.registering() ? 'register' : 'login' })
+      .pipe(finalize(() => this.platform.loading.set(false)))
+      .subscribe({
+        next: () => {
+          this.platform.toast('验证码已发送', 'success');
+          this.countdown.set(60);
+          this.timer = setInterval(() => {
+            this.countdown.update((value) => Math.max(0, value - 1));
+            if (!this.countdown() && this.timer) { clearInterval(this.timer); this.timer = null; }
+          }, 1000);
+        },
+        error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+      });
+  }
+
+  submit(): void {
+    if (!this.privacyChecked) {
+      this.platform.toast('请先阅读并同意服务协议与隐私政策', 'error');
+      return;
+    }
+    if (this.mode() === 'mobile') this.submitMobile(); else this.submitClassic();
+  }
+
+  private submitMobile(): void {
+    if (!this.validMobile(this.mobile)) { this.platform.toast('请输入正确的手机号码', 'error'); return; }
+    if (!this.code.trim()) { this.platform.toast('请输入短信验证码', 'error'); return; }
+    const action = this.registering() ? 'user_register_mobile' : 'user_login_mobile';
+    const payload = { mobile: this.mobile, vcode: this.code, code: this.inviteCode };
+    if (this.registering()) this.registerRequest('user_register_mobile', payload); else this.loginRequest('user_login_mobile', payload);
+  }
+
+  private submitClassic(): void {
+    if (!this.account.trim()) { this.platform.toast('请输入账号', 'error'); return; }
+    if (this.password.length < 6 || this.password.length > 18) { this.platform.toast('密码长度应为6至18位', 'error'); return; }
+    if (this.registering() && this.password !== this.confirmPassword) { this.platform.toast('两次输入的密码不一致', 'error'); return; }
+    if (this.registering()) {
+      const payload = { name: this.account, passwd: this.password, code: this.inviteCode };
+      this.platform.loading.set(true);
+      this.api.post('user_info_name', { uname: this.account }).subscribe({
+        next: () => { this.platform.loading.set(false); this.platform.toast('此用户名已被使用', 'error'); },
+        error: (error: ApiError) => {
+          this.platform.loading.set(false);
+          if (error.message.includes('用户不存在')) this.registerRequest('user_register', payload);
+          else this.platform.toast(`注册验证失败:${error.message}`, 'error');
+        },
+      });
+      return;
+    }
+    this.loginRequest('user_login_passwd', { name: this.account, passwd: this.password });
+  }
+
+  private loginRequest(action: 'user_login_passwd' | 'user_login_mobile', payload: Record<string, unknown>): void {
+    this.platform.loading.set(true);
+    this.auditMessage.set('');
+    this.auth.login(action, payload).pipe(finalize(() => this.platform.loading.set(false))).subscribe({
+      next: (user) => {
+        this.session.setLogin(user);
+        this.platform.toast('登录成功', 'success');
+        const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
+        void this.router.navigateByUrl(returnUrl || '/pages/home/index');
+      },
+      error: (error: ApiError) => {
+        if (error.message.includes('审核中')) this.auditMessage.set(error.message);
+        this.platform.toast(error.message, 'error');
+      },
+    });
+  }
+
+  private registerRequest(action: 'user_register' | 'user_register_mobile', payload: Record<string, unknown>): void {
+    this.platform.loading.set(true);
+    this.api.post<AppUser>(action, payload).pipe(finalize(() => this.platform.loading.set(false))).subscribe({
+      next: ({ result }) => {
+        this.session.setLogin(result);
+        this.platform.toast('注册成功', 'success');
+        void this.router.navigateByUrl('/pages/member/wxauth_after');
+      },
+      error: (error: ApiError) => this.platform.toast(`注册失败:${error.message}`, 'error'),
+    });
+  }
+
+  private validMobile(value: string): boolean { return /^1\d{10}$/.test(value.trim()); }
+}

+ 37 - 0
src/app/features/checkout/checkout-page.component.html

@@ -0,0 +1,37 @@
+<section class="checkout-page">
+  <section class="checkout-section address-section">
+    <div class="section-icon"><lucide-icon [img]="pickup() ? icons.Store : icons.MapPin" [size]="22" /></div>
+    <div><h2>{{ pickup() ? '到店自提' : '收货地址' }}</h2>
+      @if (!pickup()) { <select aria-label="选择收货地址" (change)="selectAddress($event)"><option value="">请选择地址</option>@for (address of addresses(); track address['ID'] ?? $index) { <option [value]="address['ID'] ?? address['id']">{{ address['ReceivName'] }} {{ address['MobileNum'] }} · {{ address['Provinces'] }}{{ address['Street'] }}</option> }</select><a class="manage-link" routerLink="/pages/member/receaddr">管理收货地址<lucide-icon [img]="icons.ChevronRight" [size]="15" /></a> }
+      @else { <p>商品将在门店交付,不需要填写物流地址。</p> }
+    </div><label class="switch"><input type="checkbox" [ngModel]="pickup()" (ngModelChange)="pickup.set($event)" /><span>自提</span></label>
+  </section>
+
+  <section class="checkout-section products"><h2>商品清单</h2>
+    @for (item of carts(); track item['id'] ?? item['ID'] ?? $index) { <article><img [src]="itemImage(item)" [alt]="itemName(item)" /><div><strong>{{ itemName(item) }}</strong><span>数量 × {{ itemQuantity(item) }}</span></div><b>¥{{ itemPrice(item) | number:'1.2-2' }}</b></article> }
+    @empty { <div class="empty-state"><p>购物车中没有商品</p></div> }
+  </section>
+
+  <section class="checkout-section options">
+    <label><span>优惠券</span><select aria-label="选择优惠券" (change)="selectCoupon($event)"><option value="">不使用优惠券</option>@for (coupon of availableCoupons(); track coupon['ID'] ?? $index) { <option [value]="coupon['ID']">{{ coupon['ArriveName'] || coupon['Name'] }} - ¥{{ coupon['Amount'] }}</option> }</select></label>
+    <label><span><lucide-icon [img]="icons.FileText" [size]="18" />电子普通发票</span><input type="checkbox" [ngModel]="invoiceEnabled()" (ngModelChange)="toggleInvoice($event)" /></label>
+  </section>
+
+  @if (invoiceEnabled()) {
+    <section class="checkout-section invoice-form"><h2>发票信息</h2>
+      <div class="segmented"><button type="button" [class.active]="invoiceType() === 0" (click)="changeInvoiceType(0)">个人</button><button type="button" [class.active]="invoiceType() === 1" (click)="changeInvoiceType(1)">企业</button></div>
+      <label><span>{{ invoiceType() === 0 ? '姓名' : '企业名称' }}</span><input [(ngModel)]="invoice.name" /></label>
+      <label><span>{{ invoiceType() === 0 ? '身份证号码' : '统一信用代码' }}</span><input [(ngModel)]="invoice.idnum" /></label>
+      <label><span>联系邮箱</span><input type="email" [(ngModel)]="invoice.email" /></label>
+      @if (invoiceType() === 1) {
+        <label><span>注册电话</span><input type="tel" [(ngModel)]="invoice.mobile" /></label>
+        <label><span>注册地址</span><input [(ngModel)]="invoice.address" /></label>
+        <label><span>开户银行</span><input [(ngModel)]="invoice.bankname" /></label>
+        <label><span>银行账号</span><input [(ngModel)]="invoice.bankusr" /></label>
+      }
+    </section>
+  }
+
+  <section class="totals"><div><span>商品小计</span><b>¥{{ subtotal() | number:'1.2-2' }}</b></div><div><span>优惠金额</span><b>-¥{{ discount() | number:'1.2-2' }}</b></div><div class="grand"><span>应付学习豆</span><b>{{ total() | number:'1.2-2' }}</b></div></section>
+  <footer><div><span>合计</span><strong>{{ total() | number:'1.2-2' }} 学习豆</strong></div><button type="button" class="primary-button" (click)="settle()"><lucide-icon [img]="icons.PackageCheck" [size]="19" />确认支付</button></footer>
+</section>

+ 35 - 0
src/app/features/checkout/checkout-page.component.scss

@@ -0,0 +1,35 @@
+.checkout-page { padding-bottom: 78px; }
+.checkout-section { margin-bottom: 10px; padding: 16px; background: white; }
+.checkout-section h2 { margin: 0 0 12px; font-size: 16px; letter-spacing: 0; }
+.address-section { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; align-items: start; gap: 8px; }
+.section-icon { color: var(--brand-strong); }
+.address-section select, .options select { width: 100%; min-height: 40px; padding: 7px 9px; background: white; border: 1px solid var(--line); border-radius: var(--radius-sm); }
+.address-section p { margin: 0; color: var(--ink-soft); font-size: 13px; }
+.manage-link { display: inline-flex; align-items: center; margin-top: 8px; color: var(--brand-strong); font-size: 12px; gap: 2px; }
+.switch { display: flex; align-items: center; color: var(--ink-soft); font-size: 12px; gap: 5px; }
+.switch input, .options input { width: 18px; height: 18px; accent-color: var(--brand-strong); }
+.products article { display: grid; grid-template-columns: 58px minmax(0, 1fr) auto; align-items: center; padding: 12px 0; border-top: 1px solid var(--line); gap: 10px; }
+.products img { width: 58px; height: 58px; border-radius: var(--radius-sm); }
+.products article div { display: flex; min-width: 0; flex-direction: column; gap: 5px; }
+.products article strong { overflow: hidden; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
+.products article span { color: var(--ink-soft); font-size: 12px; }
+.products article b { color: var(--danger); font-size: 13px; }
+.options label { display: grid; min-height: 50px; grid-template-columns: minmax(90px, auto) minmax(0, 1fr); align-items: center; border-bottom: 1px solid var(--line); gap: 12px; }
+.options label:last-child { grid-template-columns: minmax(0, 1fr) auto; border-bottom: 0; }
+.options label > span { display: flex; align-items: center; gap: 7px; }
+.invoice-form { display: grid; gap: 10px; }
+.segmented { display: grid; grid-template-columns: 1fr 1fr; padding: 3px; background: var(--surface-muted); border-radius: var(--radius-sm); }
+.segmented button { min-height: 34px; border: 0; color: var(--ink-soft); background: transparent; }
+.segmented button.active { color: var(--brand-strong); background: white; box-shadow: 0 1px 3px rgb(0 0 0 / 8%); }
+.invoice-form label { display: grid; grid-template-columns: 100px minmax(0, 1fr); align-items: center; gap: 10px; }
+.invoice-form input { width: 100%; min-height: 40px; padding: 8px 9px; border: 1px solid var(--line); border-radius: var(--radius-sm); }
+.totals { padding: 16px; background: white; }
+.totals div { display: flex; justify-content: space-between; padding: 7px 0; color: var(--ink-soft); font-size: 13px; }
+.totals b { color: var(--ink); }
+.totals .grand { margin-top: 8px; padding-top: 14px; color: var(--ink); border-top: 1px solid var(--line); font-size: 15px; }
+.totals .grand b { color: var(--danger); font-size: 19px; }
+footer { position: fixed; z-index: 35; right: 0; bottom: 0; left: 0; display: grid; width: min(100%, 760px); height: calc(68px + env(safe-area-inset-bottom)); grid-template-columns: minmax(0, 1fr) 150px; align-items: center; margin: 0 auto; padding: 8px 12px calc(8px + env(safe-area-inset-bottom)); background: white; border-top: 1px solid var(--line); gap: 12px; }
+footer div { display: flex; align-items: baseline; justify-content: flex-end; gap: 7px; }
+footer span { color: var(--ink-soft); font-size: 12px; }
+footer strong { color: var(--danger); font-size: 21px; }
+footer button { display: flex; align-items: center; justify-content: center; gap: 7px; }

+ 230 - 0
src/app/features/checkout/checkout-page.component.ts

@@ -0,0 +1,230 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { ActivatedRoute, Router, RouterLink } from '@angular/router';
+import { ChevronRight, FileText, LucideAngularModule, MapPin, PackageCheck, Store } from 'lucide-angular';
+import { finalize, forkJoin } from 'rxjs';
+import { ApiError, ApiService } from '../../core/api.service';
+import { AppUser } from '../../core/models';
+import { PlatformService } from '../../core/platform.service';
+import { SessionService } from '../../core/session.service';
+
+type Item = Record<string, unknown>;
+
+interface CheckoutAddress {
+  ok: boolean;
+  empty: boolean;
+  userName: string;
+  telNumber: string;
+  provinceName: string;
+  cityName: string;
+  countyName: string;
+  detailInfo: string;
+  postalCode: string;
+}
+
+interface CheckoutInvoice {
+  ok: boolean;
+  aud: number;
+  name: string;
+  mobile: string;
+  idnum: string;
+  detail: string;
+  address: string;
+  bankname: string;
+  bankusr: string;
+  email: string;
+}
+
+@Component({
+  selector: 'app-checkout-page',
+  standalone: true,
+  imports: [CommonModule, FormsModule, RouterLink, LucideAngularModule],
+  templateUrl: './checkout-page.component.html',
+  styleUrl: './checkout-page.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CheckoutPageComponent implements OnInit {
+  readonly carts = signal<Item[]>([]);
+  readonly addresses = signal<Item[]>([]);
+  readonly coupons = signal<Item[]>([]);
+  readonly selectedAddress = signal<CheckoutAddress | null>(null);
+  readonly selectedCoupon = signal<Item | null>(null);
+  readonly pickup = signal(false);
+  readonly invoiceEnabled = signal(false);
+  readonly invoiceType = signal(0);
+  readonly icons = { ChevronRight, FileText, MapPin, PackageCheck, Store };
+  readonly subtotal = computed(() => this.carts().reduce((sum, item) => sum + this.cartTotal(item), 0));
+  readonly availableCoupons = computed(() => this.coupons().filter((item) => {
+    const amount = this.subtotal();
+    return amount > Number(item['MinAmount'] ?? 0) && amount < Number(item['MaxAmount'] ?? Number.MAX_SAFE_INTEGER);
+  }));
+  readonly discount = computed(() => Number(this.selectedCoupon()?.['Amount'] ?? 0));
+  readonly total = computed(() => Math.max(0, this.subtotal() - this.discount()));
+
+  invoice: CheckoutInvoice = this.emptyInvoice();
+
+  private readonly route = inject(ActivatedRoute);
+  private readonly router = inject(Router);
+  private readonly api = inject(ApiService);
+  private readonly session = inject(SessionService);
+  private readonly platform = inject(PlatformService);
+
+  ngOnInit(): void {
+    const uid = this.session.user()?.userId;
+    const ids = this.route.snapshot.queryParamMap.get('cart') || '0';
+    this.platform.loading.set(true);
+    forkJoin({
+      cart: this.api.get<unknown>('cart_list', { ids, uid }),
+      addresses: this.api.get<unknown>('receaddr_list', { uid }),
+      coupons: this.api.get<unknown>('coupon_usrgot_list', { uid, fast: 'idle' }),
+      member: this.api.get<AppUser>('user_get', { uid }),
+    }).pipe(finalize(() => this.platform.loading.set(false))).subscribe({
+      next: ({ cart, addresses, coupons, member }) => {
+        const rows = this.findList(cart.result, 'CartDT');
+        this.carts.set(rows);
+        this.addresses.set(this.findList(addresses.result));
+        this.coupons.set(this.findList(coupons.result));
+        this.session.patchUser({ ...member.result, ...this.asObject(member.addon) });
+        if (!rows.length) this.platform.toast('购物车中没有可结算商品', 'error');
+      },
+      error: (error: ApiError) => this.platform.toast(`结算信息加载失败:${error.message}`, 'error'),
+    });
+  }
+
+  selectAddress(event: Event): void {
+    const id = (event.target as HTMLSelectElement).value;
+    const item = this.addresses().find((address) => String(address['ID'] ?? address['id']) === id);
+    if (!item) { this.selectedAddress.set(null); return; }
+    const [provinceName = '', cityName = '', countyName = ''] = String(item['Provinces'] ?? '').split(' ');
+    this.selectedAddress.set({
+      ok: true,
+      empty: false,
+      userName: String(item['ReceivName'] ?? ''),
+      telNumber: String(item['MobileNum'] ?? ''),
+      provinceName,
+      cityName,
+      countyName,
+      detailInfo: String(item['Street'] ?? ''),
+      postalCode: String(item['Zipcode'] ?? ''),
+    });
+  }
+
+  selectCoupon(event: Event): void {
+    const id = (event.target as HTMLSelectElement).value;
+    this.selectedCoupon.set(this.availableCoupons().find((item) => String(item['ID'] ?? item['id']) === id) ?? null);
+  }
+
+  toggleInvoice(enabled: boolean): void {
+    this.invoiceEnabled.set(enabled);
+    this.invoice.ok = enabled;
+  }
+
+  changeInvoiceType(type: number): void {
+    this.invoiceType.set(type);
+    this.invoice = { ...this.emptyInvoice(), ok: true, aud: type };
+  }
+
+  async settle(): Promise<void> {
+    if (!this.pickup() && !this.selectedAddress()) { this.platform.toast('请选择收货地址或到店自提', 'error'); return; }
+    if (!this.carts().length) { this.platform.toast('购物车中没有商品', 'error'); return; }
+    if (!this.validateInvoice()) return;
+    const user = this.session.user();
+    const balance = Number(user?.silverCoin ?? user?.['SilverCoin'] ?? 0);
+    if (balance < this.total()) {
+      this.platform.toast('学习豆余额不足,请先充值', 'error');
+      void this.router.navigate(['/pages/member/coin'], { queryParams: { charge: 1 } });
+      return;
+    }
+    if (!(await this.platform.confirm(`即将消耗 ${this.total().toFixed(2)} 学习豆,确认支付?`))) return;
+
+    const address = this.pickup() ? this.pickupAddress(user) : this.selectedAddress();
+    this.platform.loading.set(true);
+    this.api.post<unknown>('payment_cart', {
+      cartIds: this.carts().map((item) => item['id'] ?? item['ID']).join(','),
+      uid: user?.userId,
+      rece: JSON.stringify(address),
+      fareDT: JSON.stringify([{ storeId: '1', total: '0.00' }]),
+      use_sicon: 1,
+      invoice: this.invoiceEnabled() ? JSON.stringify({ ...this.invoice, ok: true, aud: this.invoiceType() }) : '',
+      couponid: this.selectedCoupon()?.['ID'] ?? this.selectedCoupon()?.['id'] ?? null,
+    }).subscribe({
+      next: (payment) => {
+        const order = this.parseOrder(payment.addon);
+        this.api.post('payment_success', { uid: user?.userId, orderIds: order['orderIds'], cartIds: order['cartIds'] })
+          .pipe(finalize(() => this.platform.loading.set(false)))
+          .subscribe({
+            next: () => {
+              this.platform.toast('支付成功', 'success');
+              this.refreshMember();
+              void this.router.navigate(['/pages/shop/order'], { queryParams: { id: order['orderIds'] } });
+            },
+            error: (error: ApiError) => this.platform.toast(`支付已完成,但订单状态更新失败:${error.message}`, 'error'),
+          });
+      },
+      error: (error: ApiError) => { this.platform.loading.set(false); this.platform.toast(`订单支付失败:${error.message}`, 'error'); },
+    });
+  }
+
+  itemName(item: Item): string { return String(item['proname'] ?? item['Proname'] ?? item['ProName'] ?? item['Title'] ?? '商品'); }
+  itemImage(item: Item): string { return this.api.assetUrl(item['proimg'] ?? item['Thumbnails'] ?? item['Pic']); }
+  itemQuantity(item: Item): number { return Number(item['pronum'] ?? item['Pronum'] ?? 1); }
+  itemPrice(item: Item): number { return this.cartTotal(item) / Math.max(1, this.itemQuantity(item)); }
+
+  private cartTotal(item: Item): number {
+    const quantity = this.itemQuantity(item);
+    const user = this.session.user();
+    if (Number(item['Nodeid'] ?? item['NodeID']) === 19) {
+      if (Number(user?.groupId) === 2) return 199 * quantity;
+      if (Number(user?.groupId) === 3) return 179 * quantity;
+      if (Number(user?.groupId) === 4) return 159 * quantity;
+      if (Number(user?.['vip'] ?? 0) !== 0) return Math.floor(Number(item['AllMoney'] ?? 0) * 700) / 1000;
+    }
+    return Number(item['AllMoney'] ?? Number(item['price'] ?? item['LinPrice'] ?? 0) * quantity);
+  }
+
+  private validateInvoice(): boolean {
+    if (!this.invoiceEnabled()) return true;
+    const form = this.invoice;
+    form.name = form.name.trim();
+    form.idnum = form.idnum.trim();
+    form.email = form.email.trim();
+    form.mobile = form.mobile.trim();
+    if (!form.name) { this.platform.toast(this.invoiceType() === 0 ? '请输入姓名' : '请输入企业名称', 'error'); return false; }
+    if (!form.idnum) { this.platform.toast(this.invoiceType() === 0 ? '请输入身份证号码' : '请输入统一信用代码', 'error'); return false; }
+    if (this.invoiceType() === 0 && !/^\d{17}[\dXx]$/.test(form.idnum)) { this.platform.toast('请输入正确的身份证号码', 'error'); return false; }
+    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) { this.platform.toast('请输入正确的联系邮箱', 'error'); return false; }
+    if (this.invoiceType() === 1 && !/^1\d{10}$/.test(form.mobile)) { this.platform.toast('请输入正确的企业注册电话', 'error'); return false; }
+    return true;
+  }
+
+  private pickupAddress(user: AppUser | null): CheckoutAddress {
+    return { ok: false, empty: true, userName: `${user?.honeyName ?? ''} [${user?.userName ?? ''}]`, telNumber: '', provinceName: '', cityName: '', countyName: '', detailInfo: '会员到店自提', postalCode: '' };
+  }
+
+  private emptyInvoice(): CheckoutInvoice {
+    return { ok: false, aud: 0, name: '', mobile: '', idnum: '', detail: '信息技术服务费', address: '', bankname: '', bankusr: '', email: '' };
+  }
+
+  private refreshMember(): void {
+    const uid = this.session.user()?.userId;
+    if (!uid) return;
+    this.api.get<AppUser>('user_get', { uid }).subscribe({ next: ({ result, addon }) => this.session.patchUser({ ...result, ...this.asObject(addon) }), error: () => undefined });
+  }
+
+  private findList(value: unknown, preferred?: string): Item[] {
+    if (Array.isArray(value)) return value as Item[];
+    if (value && typeof value === 'object') {
+      const object = value as Item;
+      if (preferred && Array.isArray(object[preferred])) return object[preferred] as Item[];
+      for (const nested of Object.values(object)) if (Array.isArray(nested)) return nested as Item[];
+    }
+    return [];
+  }
+
+  private asObject(value: unknown): Item { return value && typeof value === 'object' && !Array.isArray(value) ? value as Item : {}; }
+  private parseOrder(value: unknown): Item {
+    if (typeof value === 'string') { try { return JSON.parse(value) as Item; } catch { return {}; } }
+    return this.asObject(value);
+  }
+}

+ 51 - 0
src/app/features/course-center/course-center-page.component.html

@@ -0,0 +1,51 @@
+<section class="center-page">
+  @if (loading()) {
+    <div class="center-state"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="22" />正在加载课程</div>
+  } @else {
+    @if (!isCoach) {
+      <button type="button" class="library-entry" (click)="openLibrary()">
+        <i><lucide-icon [img]="icons.GraduationCap" [size]="23" /></i>
+        <span><strong>选择词库</strong><small>按学段选择教材与单元,开始选词学习</small></span>
+        <lucide-icon class="chevron" [img]="icons.ChevronRight" [size]="20" />
+      </button>
+
+      <section class="course-band">
+        <header><lucide-icon [img]="icons.BookOpen" [size]="21" /><h2>我的内容</h2><span>{{ courses().length }} 项</span></header>
+        @if (courses().length) {
+          <ul class="course-list">@for (item of courses(); track $index) { <li><button type="button" [disabled]="!courseId(item)" (click)="openCourse(item)"><span><strong>{{ courseTitle(item) }}</strong><small>{{ courseId(item) ? '进入课程,选择本次学习单元' : '暂未绑定可学习词库' }}</small></span><lucide-icon [img]="icons.ChevronRight" [size]="18" /></button></li> }</ul>
+        } @else { <p class="band-empty">暂无我的内容</p> }
+      </section>
+    }
+
+    <section class="schedule-band">
+      <header><lucide-icon [img]="isCoach ? icons.GraduationCap : icons.CalendarDays" [size]="21" /><h2>{{ isHistory ? '销课记录' : isCoach ? '陪练排课' : '伴学列表' }}</h2><span>{{ schedules().length }} 节</span></header>
+      @if (!isCoach && schedules().length) {
+        <nav class="schedule-filters" aria-label="伴学记录筛选">
+          <button type="button" [class.active]="scheduleFilter() === 'all'" (click)="setScheduleFilter('all')">全部 <span>{{ schedules().length }}</span></button>
+          <button type="button" [class.active]="scheduleFilter() === 'pending'" (click)="setScheduleFilter('pending')">待完成 <span>{{ pendingCount() }}</span></button>
+          <button type="button" [class.active]="scheduleFilter() === 'finished'" (click)="setScheduleFilter('finished')">已结束 <span>{{ finishedCount() }}</span></button>
+        </nav>
+      }
+      @if (visibleSchedules().length) {
+        @for (group of groups(); track group.date) {
+          <h3>{{ group.date }}</h3>
+          <div class="schedule-list">
+            @for (item of group.items; track $index) {
+              <article (click)="open(item)">
+                <time>{{ scheduleTime(item) }}</time>
+                <div class="schedule-copy"><strong>{{ scheduleTitle(item) }}</strong>@if (scheduleSubtitle(item)) { <span>{{ scheduleSubtitle(item) }}</span> }</div>
+                <span class="status" [class]="'status ' + statusClass(item)">{{ statusText(item) }}</span>
+                @if (isCoach) { <button type="button" class="report-button" (click)="report(item, $event)" aria-label="学习报表" title="学习报表"><lucide-icon [img]="icons.BarChart3" [size]="18" /></button> }
+                <lucide-icon class="chevron" [img]="icons.ChevronRight" [size]="18" />
+              </article>
+            }
+          </div>
+        }
+      } @else { <p class="band-empty">{{ schedules().length ? '该分类暂无伴学记录' : (isCoach ? '今天暂无排课' : '暂无伴学记录') }}</p> }
+    </section>
+
+    @if (error()) {
+      <div class="center-state error"><lucide-icon [img]="icons.AlertCircle" [size]="25" /><span>{{ error() }}</span><button type="button" class="secondary-button" (click)="load()">重新加载</button></div>
+    }
+  }
+</section>

+ 57 - 0
src/app/features/course-center/course-center-page.component.scss

@@ -0,0 +1,57 @@
+:host { display: block; min-height: 100%; background: var(--surface-muted); }
+.center-page { min-height: 100%; padding-bottom: 18px; }
+.library-entry { display: grid; width: calc(100% - 28px); min-height: 78px; grid-template-columns: 46px minmax(0, 1fr) auto; align-items: center; margin: 12px 14px 0; padding: 13px 16px; color: var(--ink); text-align: left; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); gap: 12px; }
+.library-entry > i { display: grid; width: 46px; height: 46px; color: var(--brand-strong); background: var(--brand-soft); border-radius: 8px; place-items: center; }
+.library-entry > span { display: flex; min-width: 0; flex-direction: column; gap: 5px; }
+.library-entry strong { font-size: 16px; }
+.library-entry small { overflow: hidden; color: var(--ink-soft); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
+.library-entry:active { transform: translateY(1px); }
+.course-band, .schedule-band { margin: 12px 14px 0; overflow: hidden; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); }
+.course-band > header, .schedule-band > header { display: grid; min-height: 56px; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; padding: 0 16px; color: white; background: var(--brand-strong); border-bottom: 1px solid color-mix(in srgb, var(--brand-strong) 82%, black); gap: 9px; }
+header h2 { margin: 0; color: var(--ink); font-size: 17px; letter-spacing: 0; }
+header span { color: var(--ink-soft); font-size: 12px; }
+.course-band > header h2, .schedule-band > header h2 { color: white; }
+.course-band > header span, .schedule-band > header span { color: rgb(255 255 255 / 78%); }
+.course-list { margin: 0; padding: 0; list-style: none; }
+.course-list li { border-bottom: 1px solid var(--line); }
+.course-list li:last-child { border-bottom: 0; }
+.course-list button { display: grid; width: 100%; min-height: 68px; grid-template-columns: minmax(0, 1fr) auto; align-items: center; padding: 10px 16px; color: var(--ink); text-align: left; background: transparent; border: 0; gap: 10px; }
+.course-list button > span { display: flex; min-width: 0; flex-direction: column; gap: 4px; }
+.course-list strong { overflow: hidden; font-size: 14px; text-overflow: ellipsis; white-space: nowrap; }
+.course-list small { color: var(--ink-soft); font-size: 11px; }
+.course-list button > lucide-icon { color: var(--ink-soft); }
+.course-list button:disabled { cursor: not-allowed; opacity: .58; }
+.schedule-filters { display: grid; grid-template-columns: repeat(3, 1fr); margin: 12px 14px 8px; padding: 3px; background: var(--surface-muted); border: 1px solid var(--line); border-radius: 8px; gap: 3px; }
+.schedule-filters button { min-height: 36px; padding: 6px 4px; color: var(--ink-soft); background: transparent; border: 0; border-radius: 6px; font-size: 12px; }
+.schedule-filters button span { margin-left: 2px; color: inherit; font-size: 10px; }
+.schedule-filters button.active { color: var(--brand-strong); background: white; box-shadow: 0 1px 4px rgb(20 53 45 / 10%); font-weight: 650; }
+.schedule-band h3 { margin: 0; padding: 10px 16px 7px; color: var(--ink-soft); background: var(--surface-muted); font-size: 12px; letter-spacing: 0; }
+.schedule-list article { display: grid; min-height: 76px; grid-template-columns: 64px minmax(0, 1fr) auto auto auto; align-items: center; padding: 10px 12px 10px 16px; border-bottom: 1px solid var(--line); cursor: pointer; gap: 9px; }
+.schedule-list article:last-child { border-bottom: 0; }
+.schedule-list time { font-size: 13px; font-weight: 650; }
+.schedule-copy { display: flex; min-width: 0; flex-direction: column; gap: 5px; }
+.schedule-copy strong, .schedule-copy span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.schedule-copy strong { font-size: 14px; }
+.schedule-copy span { color: var(--ink-soft); font-size: 12px; }
+.status { padding: 4px 7px; color: #606266; background: #f0f1f2; border-radius: 4px; font-size: 11px; white-space: nowrap; }
+.status-10 { color: #1f5f91; background: #e6f3fc; }
+.status-11, .status-20 { color: #82580e; background: #fff4dc; }
+.status-30 { color: #236b59; background: #e6f4ef; }
+.report-button { display: grid; width: 34px; height: 34px; padding: 0; color: var(--brand-strong); background: transparent; border: 0; place-items: center; }
+.chevron { color: var(--ink-soft); }
+.band-empty { margin: 0; padding: 28px 16px; color: var(--ink-soft); text-align: center; }
+.center-state { display: flex; min-height: 240px; align-items: center; justify-content: center; color: var(--ink-soft); flex-direction: column; gap: 10px; }
+.center-state.error { min-height: 140px; margin: 12px 14px 0; padding: 20px; background: white; border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); }
+.spin { animation: spin .8s linear infinite; }
+@keyframes spin { to { transform: rotate(360deg); } }
+@media (max-width: 420px) {
+  .schedule-list article { grid-template-columns: 54px minmax(0, 1fr) auto auto auto; gap: 7px; }
+  .schedule-list article:not(:has(.report-button)) { grid-template-columns: 54px minmax(0, 1fr) auto auto; }
+  .status { width: fit-content; }
+}
+@media (max-width: 360px) {
+  .library-entry { width: calc(100% - 20px); margin-inline: 10px; padding-inline: 13px; }
+  .library-entry small { white-space: normal; }
+  .course-band, .schedule-band, .center-state.error { margin-inline: 10px; }
+}
+@media (min-width: 760px) { .center-page { max-width: 760px; margin: 0 auto; border-inline: 1px solid var(--line); } }

+ 177 - 0
src/app/features/course-center/course-center-page.component.ts

@@ -0,0 +1,177 @@
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { AlertCircle, BarChart3, BookOpen, CalendarDays, ChevronRight, GraduationCap, LoaderCircle, LucideAngularModule } from 'lucide-angular';
+import { catchError, finalize, forkJoin, of } from 'rxjs';
+import { ApiError, ApiService } from '../../core/api.service';
+import { AppUser, PageMeta } from '../../core/models';
+import { PlatformService } from '../../core/platform.service';
+import { SessionService } from '../../core/session.service';
+
+type Item = Record<string, unknown>;
+type ScheduleFilter = 'all' | 'pending' | 'finished';
+
+@Component({
+  selector: 'app-course-center-page',
+  standalone: true,
+  imports: [CommonModule, LucideAngularModule],
+  templateUrl: './course-center-page.component.html',
+  styleUrl: './course-center-page.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CourseCenterPageComponent implements OnInit {
+  readonly meta = inject(ActivatedRoute).snapshot.data['meta'] as PageMeta;
+  readonly courses = signal<Item[]>([]);
+  readonly schedules = signal<Item[]>([]);
+  readonly scheduleFilter = signal<ScheduleFilter>('all');
+  readonly loading = signal(false);
+  readonly error = signal('');
+  readonly visibleSchedules = computed(() => this.schedules().filter((item) => {
+    if (this.scheduleFilter() === 'finished') return this.isFinished(item);
+    if (this.scheduleFilter() === 'pending') return !this.isFinished(item);
+    return true;
+  }));
+  readonly pendingCount = computed(() => this.schedules().filter((item) => !this.isFinished(item)).length);
+  readonly finishedCount = computed(() => this.schedules().filter((item) => this.isFinished(item)).length);
+  readonly icons = { AlertCircle, BarChart3, BookOpen, CalendarDays, ChevronRight, GraduationCap, LoaderCircle };
+  readonly isCoach = ['pages/home/shop', 'pages/member/ke_xiao'].includes(this.meta.path);
+  readonly isHistory = this.meta.path === 'pages/member/ke_xiao';
+
+  private readonly api = inject(ApiService);
+  private readonly router = inject(Router);
+  private readonly session = inject(SessionService);
+  private readonly platform = inject(PlatformService);
+
+  ngOnInit(): void { this.load(); }
+
+  load(): void {
+    const uid = this.session.user()?.userId;
+    if (!uid) return;
+    this.loading.set(true);
+    this.error.set('');
+    const today = this.today();
+    if (this.isCoach) {
+      const params: Record<string, unknown> = { uid, nid: 29, myfield2: `pl=${uid}`, psize: this.isHistory ? 13 : 200 };
+      if (this.isHistory) Object.assign(params, { orders: 'B.yysj=DESC', dszt: 30, xiaokejilu: 1 });
+      else params['dateFilter'] = today;
+      this.api.get<unknown>('content_list', params)
+        .pipe(finalize(() => this.loading.set(false)))
+        .subscribe({
+          next: ({ result }) => this.schedules.set(this.asList(result)),
+          error: (error: ApiError) => this.error.set(error.message),
+        });
+      return;
+    }
+
+    forkJoin({
+      courses: this.api.get<unknown>('content_list', { uid, nid: 28, myfield: `yhid=${uid}`, orders: 'id=DESC', psize: 200 })
+        .pipe(catchError((error: ApiError) => of({ retcode: -1, result: [], retmsg: error.message }))),
+      schedules: this.api.get<unknown>('content_list', { uid, nid: 29, myfield2: `szyh=${uid}`, orders: 'B.yysj=DESC', psize: 500 })
+        .pipe(catchError((error: ApiError) => of({ retcode: -1, result: [], retmsg: error.message }))),
+    }).pipe(finalize(() => this.loading.set(false))).subscribe(({ courses, schedules }) => {
+      this.courses.set(this.asList(courses.result));
+      this.schedules.set(this.asList(schedules.result));
+      const failures = [courses.retmsg, schedules.retmsg].filter(Boolean);
+      if (failures.length) this.error.set(`部分数据加载失败:${failures.join(';')}`);
+    });
+  }
+
+  groups(): Array<{ date: string; items: Item[] }> {
+    const grouped = new Map<string, Item[]>();
+    for (const item of this.visibleSchedules()) {
+      const date = String(item['bxrq'] ?? '未指定日期').slice(0, 10);
+      grouped.set(date, [...(grouped.get(date) ?? []), item]);
+    }
+    return Array.from(grouped, ([date, items]) => ({ date, items }));
+  }
+
+  courseTitle(item: Item): string {
+    const title = String(item['Title2'] ?? item['Title'] ?? '课程内容');
+    if (item['Title2'] || !title.includes('@')) return title;
+    const parts = title.split('@');
+    const first = parts[0].slice(parts[0].lastIndexOf('|') + 1);
+    return `【${first}】${parts.at(-1) ?? ''}`;
+  }
+
+  courseId(item: Item): string {
+    return String(item['kcid'] ?? item['kcId'] ?? item['NodeID'] ?? item['nodeId'] ?? '');
+  }
+
+  openCourse(item: Item): void {
+    const id = this.courseId(item);
+    if (!id) {
+      this.platform.toast('该内容暂未绑定可学习词库', 'error');
+      return;
+    }
+    void this.router.navigate(['/pages/stu/stu_list'], { queryParams: { nid: id } });
+  }
+
+  openLibrary(): void {
+    void this.router.navigate(['/pages/stu/index']);
+  }
+
+  scheduleTitle(item: Item): string { return String(item['Title'] ?? item['ctitle'] ?? '排课'); }
+  scheduleTime(item: Item): string { return String(item['sdsd'] ?? item['yysj'] ?? '--'); }
+  scheduleSubtitle(item: Item): string { return String(item['Subtitle'] ?? item['plxm'] ?? item['honeyname'] ?? ''); }
+  scheduleId(item: Item): unknown { return item['GeneralID'] ?? item['ID'] ?? item['id']; }
+
+  statusText(item: Item): string {
+    return ({ 0: '未开始', 10: '已开始', 11: '已上课', 20: '已评价', 30: '已结课' } as Record<number, string>)[Number(item['dszt'] ?? 0)] ?? '--';
+  }
+
+  statusClass(item: Item): string { return `status-${Number(item['dszt'] ?? 0)}`; }
+
+  isFinished(item: Item): boolean { return Number(item['dszt'] ?? 0) >= 30; }
+
+  setScheduleFilter(filter: ScheduleFilter): void { this.scheduleFilter.set(filter); }
+
+  open(item: Item): void {
+    if (!this.isCoach) {
+      void this.router.navigate(['/pages/shop/stu_get_order'], { queryParams: { id: this.scheduleId(item) } });
+      return;
+    }
+    if (Number(item['dszt'] ?? 0) <= 10 && String(item['bxrq'] ?? '').slice(0, 10) !== this.today()) {
+      this.platform.toast('课程未开始', 'error');
+      return;
+    }
+    this.loadStudentAndNavigate(item, '/pages/shop/get_order', 'id');
+  }
+
+  report(item: Item, event: Event): void {
+    event.stopPropagation();
+    if (Number(item['dszt'] ?? 0) > 10) {
+      void this.router.navigate(['/pages/shop/baobiao'], { queryParams: { sid: this.scheduleId(item) } });
+      return;
+    }
+    this.loadStudentAndNavigate(item, '/pages/shop/baobiao', 'id');
+  }
+
+  private loadStudentAndNavigate(item: Item, path: string, queryKey: 'id' | 'sid'): void {
+    const studentId = Number(item['szyh'] ?? 0);
+    if (!studentId) {
+      void this.router.navigate([path], { queryParams: { [queryKey]: this.scheduleId(item) } });
+      return;
+    }
+    this.platform.loading.set(true);
+    this.api.post<AppUser>('user_get', { uid: studentId }).pipe(finalize(() => this.platform.loading.set(false))).subscribe({
+      next: ({ result }) => {
+        this.session.setStudent(result);
+        void this.router.navigate([path], { queryParams: { [queryKey]: this.scheduleId(item) } });
+      },
+      error: (error: ApiError) => this.platform.toast(error.message, 'error'),
+    });
+  }
+
+  private asList(value: unknown): Item[] {
+    if (Array.isArray(value)) return value.filter((item): item is Item => Boolean(item) && typeof item === 'object');
+    if (value && typeof value === 'object') {
+      for (const nested of Object.values(value)) if (Array.isArray(nested)) return nested as Item[];
+    }
+    return [];
+  }
+
+  private today(): string {
+    const now = new Date();
+    return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
+  }
+}

+ 232 - 0
src/app/features/english-reading/english-reading.service.ts

@@ -0,0 +1,232 @@
+import { Injectable } from '@angular/core';
+import { Subscription } from 'rxjs';
+import Mustache from 'mustache';
+import { FmodeAiService } from './fmode-ai.service';
+import { NcloudReadingService } from './ncloud-reading.service';
+import {
+  ReadingArticle, ReadingHistoryEntry, ReadingLog, ReadingOption, ReadingProgress,
+  ReadingQuestion, ReadingRouteParams,
+} from './reading.models';
+import { readingStage, readingWordLimit, stageQuestionLimit } from './reading.utils';
+
+export const ARTICLE_TEMPLATE = 'english-reading-article-tpl';
+export const SUPPLY_TEMPLATE = 'english-reading-article-supply-tpl';
+export const QUESTION_TEMPLATE = 'english-reading-question-json-tpl';
+
+interface PromptRecord { objectId: string; template?: string; [key: string]: unknown }
+interface GeneratedQuestion {
+  title?: string; type?: string; difficulty?: string; wordList?: string[];
+  options?: ReadingOption[]; answer?: string;
+}
+
+@Injectable({ providedIn: 'root' })
+export class EnglishReadingService {
+  constructor(
+    private readonly cloud: NcloudReadingService,
+    private readonly ai: FmodeAiService,
+  ) {}
+
+  async findArticles(wordList: string[], limit = 100): Promise<ReadingArticle[]> {
+    const where: Record<string, unknown> = {
+      'createOptions.tpl': ARTICLE_TEMPLATE,
+      user: this.cloud.userPointer,
+      isDeleted: { '$ne': true },
+    };
+    if (wordList.length) where['createOptions.params.wordList'] = { '$in': wordList };
+    return this.cloud.query<ReadingArticle>('SurveyItem', where, { order: '-createdAt', limit });
+  }
+
+  async duplicateExists(params: ReadingRouteParams): Promise<boolean> {
+    try {
+      const results = await this.cloud.query<ReadingArticle>('SurveyItem', {
+        'createOptions.tpl': ARTICLE_TEMPLATE,
+        'createOptions.params.level': params.level,
+        'createOptions.params.type': params.type,
+        'createOptions.params.theme': params.theme,
+        'createOptions.params.wordList': { '$in': params.wordList },
+        user: this.cloud.userPointer,
+        isDeleted: { '$ne': true },
+      }, { limit: 1 });
+      return results.length > 0;
+    } catch { return false; }
+  }
+
+  async createArticle(params: ReadingRouteParams, onProgress: (progress: ReadingProgress) => void): Promise<ReadingArticle> {
+    const createParams = {
+      wordList: params.wordList,
+      count: readingWordLimit(params.level, params.wordList.length),
+      stage: readingStage(params.level),
+      level: params.level,
+      type: params.type,
+      theme: params.theme,
+    };
+    if (!params.renew) {
+      const existing = await this.cloud.query<ReadingArticle>('SurveyItem', {
+        'createOptions.tpl': ARTICLE_TEMPLATE,
+        'createOptions.params.level': params.level,
+        'createOptions.params.type': params.type,
+        'createOptions.params.theme': params.theme,
+        'createOptions.params.wordList': { '$in': params.wordList },
+        user: this.cloud.userPointer,
+        isDeleted: { '$ne': true },
+      }, { order: '-createdAt', limit: 1 });
+      if (existing[0]) {
+        onProgress({ task: 'article', message: '已加载历史文章', content: existing[0].content });
+        return existing[0];
+      }
+    }
+
+    const prompt = await this.formatPrompt(ARTICLE_TEMPLATE, createParams);
+    const content = await this.collectStream(prompt, (value) => {
+      onProgress({ task: 'article', message: `正在撰写短文 ${value.length} 字符`, content: value });
+    });
+    const lines = content.split('\n');
+    const title = (lines.shift() ?? 'Reading Exercise').replace(/<[^>]*>|[*_]{1,3}/g, '').trim();
+    const body = lines.join('\n');
+    if (!body) throw new Error('生成的文章内容为空');
+    return this.cloud.create<ReadingArticle>('SurveyItem', {
+      type: 'reading', title, content: body,
+      createOptions: { tpl: ARTICLE_TEMPLATE, params: createParams },
+      user: this.cloud.userPointer,
+    });
+  }
+
+  async supplyMissingWords(article: ReadingArticle, words: string[], onProgress: (progress: ReadingProgress) => void): Promise<ReadingArticle> {
+    if (!words.length) return article;
+    const params = article.createOptions.params;
+    const prompt = await this.formatPrompt(SUPPLY_TEMPLATE, {
+      content: article.content, wordList: words, stage: params.stage, level: params.level,
+    });
+    const content = await this.collectStream(prompt, (value) => {
+      onProgress({ task: 'supply', message: `正在补全词汇 ${value.length} 字符`, content: value });
+    });
+    return this.cloud.update('SurveyItem', article, { content });
+  }
+
+  async getQuestions(article: ReadingArticle): Promise<ReadingQuestion[]> {
+    return this.cloud.query<ReadingQuestion>('SurveyItem', {
+      parent: this.cloud.pointer('SurveyItem', article.objectId),
+      isDeleted: { '$ne': true },
+    }, { order: 'index' });
+  }
+
+  async createQuestions(article: ReadingArticle, onProgress: (progress: ReadingProgress) => void): Promise<ReadingQuestion[]> {
+    const existing = await this.getQuestions(article);
+    if (existing.length) return existing;
+    const params = article.createOptions.params;
+    const prompt = await this.formatPrompt(QUESTION_TEMPLATE, {
+      title: article.title,
+      content: article.content,
+      ...params,
+      count: 5,
+      stageQuestionLimit: stageQuestionLimit(params.level),
+    });
+    onProgress({ task: 'questions', message: '正在设计 5 道选择题' });
+    const schema = '{"count":5,"stage":"年级阶段","questions":[{"title":"英文题目","type":"题型","difficulty":"easy|normal|difficult","wordList":[],"options":[{"check":true,"value":"英文选项","answer":"中文解析"}],"answer":"中文题目解析"}]}';
+    const generated = await this.ai.json(prompt, schema, 2, (content) => {
+      onProgress({ task: 'questions', message: `正在设计选择题 ${content.length} 字符` });
+    });
+    const list = generated['questions'];
+    if (!Array.isArray(list) || !list.length) throw new Error('AI 返回的题目列表为空');
+    const questions: ReadingQuestion[] = [];
+    for (let index = 0; index < list.length; index += 1) {
+      const item = list[index] as GeneratedQuestion;
+      if (!item.title || !Array.isArray(item.options) || item.options.length < 2) {
+        throw new Error(`第 ${index + 1} 题格式不完整`);
+      }
+      questions.push(await this.cloud.create<ReadingQuestion>('SurveyItem', {
+        parent: this.cloud.pointer('SurveyItem', article.objectId),
+        type: 'reading-item', index, title: item.title,
+        difficulty: item.difficulty, answer: item.answer,
+        options: item.options,
+        createOptions: { tpl: QUESTION_TEMPLATE, params: { type: item.type, wordList: item.wordList } },
+        user: this.cloud.userPointer,
+      }));
+    }
+    return questions;
+  }
+
+  async latestLog(articleId: string): Promise<ReadingLog | null> {
+    const logs = await this.cloud.query<ReadingLog>('SurveyLog', {
+      surveyItem: this.cloud.pointer('SurveyItem', articleId),
+      user: this.cloud.userPointer,
+    }, { order: '-createdAt', limit: 1 });
+    return logs[0] ?? null;
+  }
+
+  async saveLog(article: ReadingArticle, questions: ReadingQuestion[], answers: string[], score: number): Promise<ReadingLog> {
+    const answer = Object.fromEntries(questions.map((question, index) => [question.objectId, answers[index] ?? '']));
+    return this.cloud.create<ReadingLog>('SurveyLog', {
+      surveyItem: this.cloud.pointer('SurveyItem', article.objectId),
+      answer,
+      right: score,
+      wrong: questions.length - score,
+      user: this.cloud.userPointer,
+      type: 'exam',
+      grade: Number(((score / questions.length) * 100).toFixed(2)),
+    });
+  }
+
+  async history(wordList: string[]): Promise<ReadingHistoryEntry[]> {
+    return this.historyPage(wordList, 0, 100);
+  }
+
+  async historyPage(wordList: string[], skip = 0, limit = 10): Promise<ReadingHistoryEntry[]> {
+    const where: Record<string, unknown> = {
+      'createOptions.tpl': ARTICLE_TEMPLATE,
+      user: this.cloud.userPointer,
+      isDeleted: { '$ne': true },
+    };
+    if (wordList.length) where['createOptions.params.wordList'] = { '$in': wordList };
+    const articles = await this.cloud.query<ReadingArticle>('SurveyItem', where, { order: '-createdAt', skip, limit });
+    if (!articles.length) return [];
+    const pointers = articles.map((article) => this.cloud.pointer('SurveyItem', article.objectId));
+    const logs = await this.cloud.query<ReadingLog>('SurveyLog', {
+      surveyItem: { '$in': pointers },
+      user: this.cloud.userPointer,
+    }, { order: '-createdAt', limit: 100 });
+    return articles.map((article) => {
+      const articleLogs = logs.filter((log) => log.surveyItem?.objectId === article.objectId);
+      return {
+        article,
+        bestScore: Math.max(0, ...articleLogs.map((log) => Number(log.grade ?? 0))),
+        attemptCount: articleLogs.length,
+        lastAttempt: new Date(articleLogs[0]?.createdAt || article.createdAt || 0),
+      };
+    }).sort((left, right) => Number(right.lastAttempt) - Number(left.lastAttempt));
+  }
+
+  private async formatPrompt(code: string, values: Record<string, unknown>): Promise<string> {
+    const templates = await this.cloud.query<PromptRecord>('PromptTemplate', { code }, { limit: 1 });
+    const template = templates[0]?.template;
+    if (!template) throw new Error(`未找到提示词模板:${code}`);
+    return Mustache.render(template, values);
+  }
+
+  private collectStream(prompt: string, onContent: (content: string) => void): Promise<string> {
+    return new Promise((resolve, reject) => {
+      let timer: ReturnType<typeof setTimeout>;
+      let subscription: Subscription | undefined;
+      const resetTimer = () => {
+        clearTimeout(timer);
+        timer = setTimeout(() => {
+          subscription?.unsubscribe();
+          reject(new Error('生成服务响应超时'));
+        }, 40000);
+      };
+      resetTimer();
+      subscription = this.ai.stream(prompt).subscribe({
+        next: (message) => {
+          resetTimer();
+          onContent(message.content);
+          if (message.complete) {
+            clearTimeout(timer);
+            resolve(message.content);
+          }
+        },
+        error: (error) => { clearTimeout(timer); reject(error); },
+        complete: () => clearTimeout(timer),
+      });
+    });
+  }
+}

+ 157 - 0
src/app/features/english-reading/fmode-ai.service.ts

@@ -0,0 +1,157 @@
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+
+const API_BASE = '/fmode-api';
+const MODEL = 'doubao-seed-2-0-lite-260215';
+
+export interface AiStreamMessage {
+  content: string;
+  complete: boolean;
+}
+
+@Injectable({ providedIn: 'root' })
+export class FmodeAiService {
+  stream(prompt: string): Observable<AiStreamMessage> {
+    return new Observable((observer) => {
+      const controller = new AbortController();
+      void this.readStream(prompt, controller.signal, observer).catch((error: unknown) => observer.error(error));
+      return () => controller.abort();
+    });
+  }
+
+  async json(prompt: string, schema: string, retries = 2, onMessage?: (content: string) => void): Promise<Record<string, unknown>> {
+    const jsonPrompt = `${prompt}\n请严格按以下结构返回一个完整 JSON 对象,不要使用 Markdown 代码块:\n${schema}`;
+    for (let attempt = 0; attempt <= retries; attempt += 1) {
+      try {
+        const response = await fetch(`${API_BASE}/v1/chat/completions`, {
+          method: 'POST',
+          headers: { 'Content-Type': 'application/json' },
+          body: JSON.stringify({
+            model: MODEL,
+            messages: [{ role: 'user', content: jsonPrompt }],
+            stream: false,
+            max_tokens: 32768,
+            response_format: { type: 'json_object' },
+            thinking: { type: 'disabled' },
+          }),
+        });
+        if (!response.ok) throw new Error(await this.apiError(response));
+        const data = await response.json() as { choices?: Array<{ message?: { content?: string } }> };
+        const content = data.choices?.[0]?.message?.content ?? '';
+        onMessage?.(content);
+        return this.extractJson(content);
+      } catch (error) {
+        if (attempt === retries) throw error;
+      }
+    }
+    throw new Error('题目生成失败');
+  }
+
+  private async readStream(prompt: string, signal: AbortSignal, observer: {
+    next(value: AiStreamMessage): void; complete(): void; error(error: unknown): void;
+  }): Promise<void> {
+    const response = await fetch(`${API_BASE}/v1/chat/completions`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      signal,
+      body: JSON.stringify({
+        model: MODEL,
+        messages: [{ role: 'user', content: prompt }],
+        stream: true,
+        max_tokens: 32768,
+        thinking: { type: 'disabled' },
+      }),
+    });
+    if (!response.ok) throw new Error(await this.apiError(response));
+    if (!response.body) throw new Error('生成服务未返回内容');
+
+    const reader = response.body.getReader();
+    const decoder = new TextDecoder();
+    let buffer = '';
+    let content = '';
+    let done = false;
+    while (!done) {
+      const chunk = await reader.read();
+      done = chunk.done;
+      buffer += decoder.decode(chunk.value, { stream: !done });
+      const lines = buffer.split('\n');
+      buffer = lines.pop() ?? '';
+      for (const line of lines) {
+        if (!line.startsWith('data:')) continue;
+        const payload = line.slice(5).trim();
+        if (!payload || payload === '[DONE]') continue;
+        try {
+          const parsed = JSON.parse(payload) as { choices?: Array<{ delta?: { content?: string } }> };
+          content += parsed.choices?.[0]?.delta?.content ?? '';
+          observer.next({ content, complete: false });
+        } catch { /* Ignore malformed event data. */ }
+      }
+    }
+    observer.next({ content, complete: true });
+    observer.complete();
+  }
+
+  private extractJson(content: string): Record<string, unknown> {
+    const normalized = content.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '');
+    try { return JSON.parse(normalized) as Record<string, unknown>; }
+    catch {
+      const start = normalized.indexOf('{');
+      if (start < 0) throw new Error('AI 返回内容中未找到 JSON 对象');
+      let count = 0;
+      let inString = false;
+      let escaped = false;
+      let end = start;
+      for (let index = start; index < normalized.length; index += 1) {
+        const character = normalized[index];
+        if (escaped) { escaped = false; continue; }
+        if (character === '\\') { escaped = true; continue; }
+        if (character === '"') { inString = !inString; continue; }
+        if (inString) continue;
+        if (character === '{') count += 1;
+        else if (character === '}') count -= 1;
+        if (count === 0) { end = index; break; }
+      }
+      if (count !== 0) throw new Error('AI 返回的 JSON 不完整');
+      const jsonText = normalized.slice(start, end + 1);
+      try { return JSON.parse(jsonText) as Record<string, unknown>; }
+      catch {
+        try { return JSON.parse(this.repairUnescapedStringQuotes(jsonText)) as Record<string, unknown>; }
+        catch { throw new Error('AI 返回的 JSON 格式无法解析'); }
+      }
+    }
+  }
+
+  private repairUnescapedStringQuotes(jsonText: string): string {
+    let repaired = '';
+    let inString = false;
+    let escaped = false;
+    for (let index = 0; index < jsonText.length; index += 1) {
+      const character = jsonText[index];
+      if (escaped) { repaired += character; escaped = false; continue; }
+      if (character === '\\') { repaired += character; escaped = true; continue; }
+      if (character !== '"') { repaired += character; continue; }
+      if (!inString) { inString = true; repaired += character; continue; }
+      const next = this.nextNonSpaceCharacter(jsonText, index + 1);
+      if (!next || [':', ',', '}', ']'].includes(next)) {
+        inString = false;
+        repaired += character;
+      } else {
+        repaired += '\\"';
+      }
+    }
+    return repaired;
+  }
+
+  private nextNonSpaceCharacter(value: string, start: number): string {
+    for (let index = start; index < value.length; index += 1) {
+      if (!/\s/.test(value[index])) return value[index];
+    }
+    return '';
+  }
+
+  private async apiError(response: Response): Promise<string> {
+    const message = await response.text().catch(() => '');
+    if (response.status === 503 && message.includes('FMODE_API_TOKEN')) return '阅读生成服务尚未配置,请联系管理员';
+    return `生成服务请求失败(${response.status})`;
+  }
+}

+ 48 - 0
src/app/features/english-reading/ncloud-reading.service.spec.ts

@@ -0,0 +1,48 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { NcloudReadingService } from './ncloud-reading.service';
+
+describe('NcloudReadingService', () => {
+  let service: NcloudReadingService;
+  let http: HttpTestingController;
+
+  beforeEach(() => {
+    localStorage.removeItem('NCloud/dev/User');
+    TestBed.configureTestingModule({ providers: [provideHttpClient(), provideHttpClientTesting()] });
+    service = TestBed.inject(NcloudReadingService);
+    http = TestBed.inject(HttpTestingController);
+  });
+
+  afterEach(() => {
+    http.verify();
+    localStorage.removeItem('NCloud/dev/User');
+  });
+
+  it('logs in with the source-compatible derived account and persists the session', async () => {
+    const result = service.ensureUser({ userId: 7, mobile: '13800000000', honeyName: '小树' });
+    await Promise.resolve();
+    const request = http.expectOne('/ncloud/login');
+    expect(request.request.method).toBe('POST');
+    expect(request.request.headers.get('X-Parse-Application-Id')).toBe('7pIbDBJmKx_main');
+    expect(request.request.body.username).toBe('7pIbDBJmKx-13800000000');
+    expect(request.request.body.password).toBe(btoa('7pIbDBJmKx-13800000000'));
+    request.flush({ objectId: 'user-1', sessionToken: 'token-1', mobile: '13800000000' });
+    expect((await result).objectId).toBe('user-1');
+    expect(JSON.parse(localStorage.getItem('NCloud/dev/User') || '{}').sessionToken).toBe('token-1');
+  });
+
+  it('sends Parse-compatible filters and the active session token', async () => {
+    const login = service.ensureUser({ userId: 7, mobile: '13800000000' });
+    await Promise.resolve();
+    http.expectOne('/ncloud/login').flush({ objectId: 'user-1', sessionToken: 'token-1' });
+    await login;
+    const query = service.query('SurveyItem', { type: 'reading', isDeleted: { '$ne': true } }, { order: '-createdAt', limit: 3 });
+    const request = http.expectOne((candidate) => candidate.url === '/ncloud/classes/SurveyItem');
+    expect(JSON.parse(request.request.params.get('where') || '{}').type).toBe('reading');
+    expect(request.request.params.get('order')).toBe('-createdAt');
+    expect(request.request.headers.get('X-Parse-Session-Token')).toBe('token-1');
+    request.flush({ results: [] });
+    expect(await query).toEqual([]);
+  });
+});

+ 139 - 0
src/app/features/english-reading/ncloud-reading.service.ts

@@ -0,0 +1,139 @@
+import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { firstValueFrom } from 'rxjs';
+import { AppUser } from '../../core/models';
+import { NcloudPointer, NcloudRecord, NcloudUser } from './reading.models';
+
+const APP_ID = '7pIbDBJmKx_main';
+const COMPANY_ID = '7pIbDBJmKx';
+const USER_CACHE_KEY = 'NCloud/dev/User';
+const IS_LOCAL = ['localhost', '127.0.0.1'].includes(globalThis.location?.hostname ?? '');
+const API_BASE = IS_LOCAL ? '/ncloud' : 'https://server.xiaoshu.pro/parse';
+
+interface QueryResponse<T> { results: T[] }
+
+@Injectable({ providedIn: 'root' })
+export class NcloudReadingService {
+  private currentUser: NcloudUser | null = this.readCachedUser();
+
+  constructor(private readonly http: HttpClient) {}
+
+  get user(): NcloudUser | null { return this.currentUser; }
+
+  get userPointer(): NcloudPointer {
+    if (!this.currentUser?.objectId) throw new Error('NCloud 用户尚未登录');
+    return this.pointer('_User', this.currentUser.objectId);
+  }
+
+  adoptSessionToken(token: string): void {
+    if (token) this.currentUser = { ...(this.currentUser ?? { objectId: '' }), sessionToken: token };
+  }
+
+  async ensureUser(appUser: AppUser | null, tokenHint = '', mobileHint = ''): Promise<NcloudUser> {
+    if (tokenHint) this.adoptSessionToken(tokenHint);
+    const authenticated = await this.tryCurrentUser();
+    if (authenticated) return authenticated;
+
+    const mobile = String(mobileHint || appUser?.mobile || appUser?.userName || '').trim();
+    if (!mobile) throw new Error('缺少联系电话,无法连接阅读学习账户');
+    const username = `${COMPANY_ID}-${mobile}`;
+    const password = btoa(username);
+    const nickname = String(appUser?.honeyName || appUser?.userName || '默认昵称');
+    try {
+      const user = await firstValueFrom(this.http.post<NcloudUser>(`${API_BASE}/login`, { username, password }, {
+        headers: this.headers(false),
+      }));
+      return this.persistUser(user);
+    } catch {
+      const user = await firstValueFrom(this.http.post<NcloudUser>(`${API_BASE}/users`, {
+        username,
+        password,
+        mobile,
+        nickname,
+        company: this.pointer('Company', COMPANY_ID),
+        type: 'user',
+      }, { headers: this.headers(false) }));
+      return this.persistUser({ ...user, username, mobile, nickname });
+    }
+  }
+
+  async updateUser(patch: Partial<NcloudUser>): Promise<NcloudUser> {
+    if (!this.currentUser?.objectId) throw new Error('NCloud 用户尚未登录');
+    await firstValueFrom(this.http.put(`${API_BASE}/users/${this.currentUser.objectId}`, patch, {
+      headers: this.headers(),
+    }));
+    return this.persistUser({ ...this.currentUser, ...patch });
+  }
+
+  async query<T extends NcloudRecord>(className: string, where: Record<string, unknown>, options: {
+    order?: string; limit?: number; skip?: number;
+  } = {}): Promise<T[]> {
+    let params = new HttpParams().set('where', JSON.stringify(where));
+    if (options.order) params = params.set('order', options.order);
+    if (options.limit !== undefined) params = params.set('limit', options.limit);
+    if (options.skip !== undefined) params = params.set('skip', options.skip);
+    const response = await firstValueFrom(this.http.get<QueryResponse<T>>(`${API_BASE}/classes/${className}`, {
+      headers: this.headers(), params,
+    }));
+    return response.results ?? [];
+  }
+
+  async get<T extends NcloudRecord>(className: string, objectId: string): Promise<T> {
+    return firstValueFrom(this.http.get<T>(`${API_BASE}/classes/${className}/${objectId}`, {
+      headers: this.headers(),
+    }));
+  }
+
+  async create<T extends NcloudRecord>(className: string, value: Omit<T, 'objectId'>): Promise<T> {
+    const response = await firstValueFrom(this.http.post<Pick<T, 'objectId' | 'createdAt'>>(
+      `${API_BASE}/classes/${className}`, value, { headers: this.headers() },
+    ));
+    return { ...value, ...response } as T;
+  }
+
+  async update<T extends NcloudRecord>(className: string, value: T, patch: Partial<T>): Promise<T> {
+    const response = await firstValueFrom(this.http.put<Partial<T>>(
+      `${API_BASE}/classes/${className}/${value.objectId}`, patch, { headers: this.headers() },
+    ));
+    return { ...value, ...patch, ...response };
+  }
+
+  pointer(className: string, objectId: string): NcloudPointer {
+    return { __type: 'Pointer', className, objectId };
+  }
+
+  private async tryCurrentUser(): Promise<NcloudUser | null> {
+    if (!this.currentUser?.sessionToken) return null;
+    try {
+      const response = await firstValueFrom(this.http.get<NcloudUser>(`${API_BASE}/users/me`, {
+        headers: this.headers(),
+      }));
+      return this.persistUser({ ...this.currentUser, ...response });
+    } catch {
+      this.currentUser = null;
+      localStorage.removeItem(USER_CACHE_KEY);
+      return null;
+    }
+  }
+
+  private headers(withSession = true): HttpHeaders {
+    let headers = new HttpHeaders({ 'X-Parse-Application-Id': APP_ID, 'Content-Type': 'application/json' });
+    if (withSession && this.currentUser?.sessionToken) {
+      headers = headers.set('X-Parse-Session-Token', this.currentUser.sessionToken);
+    }
+    return headers;
+  }
+
+  private persistUser(user: NcloudUser): NcloudUser {
+    this.currentUser = user;
+    localStorage.setItem(USER_CACHE_KEY, JSON.stringify(user));
+    return user;
+  }
+
+  private readCachedUser(): NcloudUser | null {
+    try {
+      const user = JSON.parse(localStorage.getItem(USER_CACHE_KEY) || 'null') as NcloudUser | null;
+      return user?.sessionToken ? user : null;
+    } catch { return null; }
+  }
+}

+ 33 - 0
src/app/features/english-reading/reading-exercise.component.html

@@ -0,0 +1,33 @@
+<section class="exercise-page">
+  <header class="exercise-toolbar"><span>{{ article()?.content?.split(' ')?.length || 0 }} words</span><strong>Reading Exercise</strong><button type="button" (click)="print()" [disabled]="!article()?.objectId"><lucide-icon [img]="icons.FileDown" [size]="17" />打印</button></header>
+
+  @if (error()) {
+    <div class="page-state error-state"><lucide-icon [img]="icons.XCircle" [size]="30" /><strong>加载阅读练习失败</strong><p>{{ error() }}</p><button type="button" class="secondary-button" (click)="load()">重新加载</button></div>
+  } @else {
+    <article class="article-section">
+      <div class="meta-row">@if (article()?.createOptions?.params?.type) { <span>{{ article()?.createOptions?.params?.type }}</span> } @if (article()?.createOptions?.params?.theme) { <span>{{ article()?.createOptions?.params?.theme }}</span> } @if (questions().length) { <span>{{ questions().length }} 道题</span> }</div>
+      @if (article()?.title) { <h2>{{ article()?.title }}</h2> } @else { <div class="skeleton title-skeleton"></div> }
+      @if (article()?.content) { <div class="article-content" [innerHTML]="articleHtml"></div> } @else { <div class="text-skeleton"><i></i><i></i><i></i><i></i></div> }
+    </article>
+
+    @if (loading()) {
+      <div class="generation-state"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="25" /><div><strong>{{ progress().message }}</strong><p>{{ progress().task === 'article' ? '1' : progress().task === 'supply' ? '2' : '3' }}/3 · {{ loadingTip() }}</p></div></div>
+    } @else {
+      <section class="questions-section">
+        @for (question of questions(); track question.objectId; let questionIndex = $index) {
+          <article class="question-block">
+            <header><span>Q{{ questionIndex + 1 }}</span><strong>{{ question.title }}</strong>@if (submitted()) { <lucide-icon [img]="isCorrect(question, questionIndex) ? icons.CheckCircle2 : icons.XCircle" [class.correct]="isCorrect(question, questionIndex)" [size]="21" /> }</header>
+            <div class="options-grid">@for (option of question.options; track option.value; let optionIndex = $index) { <button type="button" class="option" [class.selected]="answers[questionIndex] === option.value" [class.correct-option]="submitted() && option.check" [class.wrong-option]="submitted() && answers[questionIndex] === option.value && !option.check" (click)="selectOption(questionIndex, option.value)"><span>{{ optionLabels[optionIndex] }}</span><b>{{ option.value }}</b>@if (submitted() && option.check) { <lucide-icon [img]="icons.CheckCircle2" [size]="18" /> }</button> }</div>
+            @if (submitted()) { <div class="answer-analysis"><strong>解析</strong><p>{{ question.answer || '请结合原文定位正确答案。' }}</p>@for (option of question.options; track option.value) { @if (option.answer) { <small><b>{{ option.value }}:</b>{{ option.answer }}</small> } }</div> }
+          </article>
+        }
+      </section>
+    }
+  }
+
+  @if (!loading() && !error()) { <footer class="answer-footer"><button type="button" class="primary-button submit-button" [class.retry]="submitted()" [disabled]="!submitted() && answeredCount < questions().length" (click)="submit()"><lucide-icon [img]="submitted() ? icons.RotateCcw : icons.CheckCircle2" [size]="18" />{{ submitted() ? (score() === questions().length ? '已完成' : '再试一次') : '提交答案' }}({{ answeredCount }}/{{ questions().length }})</button><button type="button" class="footer-tool" (click)="openHistory()"><lucide-icon [img]="icons.History" [size]="19" /><span>{{ 3 - remainingArticles() }}/3</span></button>@if (submitted() && score() === questions().length && remainingArticles() > 0) { <button type="button" class="footer-tool new-button" (click)="createNewArticle()"><lucide-icon [img]="icons.CirclePlus" [size]="19" /><span>新文章</span></button> }</footer> }
+</section>
+
+@if (historyOpen()) { <div class="dialog-backdrop" (click)="closeHistory()"><section class="history-dialog" role="dialog" aria-modal="true" (click)="$event.stopPropagation()"><header><div><h2>阅读历史</h2><p>相同生词最多生成三篇文章</p></div><button type="button" (click)="closeHistory()" aria-label="关闭" title="关闭"><lucide-icon [img]="icons.X" [size]="20" /></button></header><div class="history-list">@if (historyLoading()) { <div class="dialog-state"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="24" />正在加载</div> } @else if (!historyItems().length) { <div class="dialog-state"><lucide-icon [img]="icons.BookOpen" [size]="28" />暂无阅读历史</div> } @else { @for (item of historyItems(); track item.article.objectId) { <button type="button" class="history-item" (click)="selectHistory(item)"><div><strong>{{ item.article.title }}</strong><p>{{ item.article.createOptions.params.level }} · {{ item.article.createOptions.params.type }}</p></div><span>{{ item.attemptCount ? item.bestScore + '分' : '未练习' }}</span><lucide-icon [img]="icons.ChevronRight" [size]="18" /></button> } @if (historyHasMore()) { <button type="button" class="load-more" [disabled]="historyLoadingMore()" (click)="loadMoreHistory()">{{ historyLoadingMore() ? '加载中...' : '加载更多' }}</button> } @else { <p class="no-more">已经显示全部历史记录</p> } }</div></section></div> }
+
+@if (dialog(); as popup) { <div class="result-backdrop"><section class="result-dialog" [class]="'result-dialog ' + popup.tone" role="dialog" aria-modal="true"><div class="result-icon">@if (popup.tone === 'success') { <lucide-icon [img]="icons.CheckCircle2" [size]="38" /> } @else { <lucide-icon [img]="icons.XCircle" [size]="38" /> }</div><h2>{{ popup.title }}</h2><p>{{ popup.message }}</p><button type="button" class="primary-button" (click)="handleDialog()">{{ popup.buttonText }}</button></section></div> }

+ 58 - 0
src/app/features/english-reading/reading-exercise.component.scss

@@ -0,0 +1,58 @@
+:host { display: block; min-height: 100%; background: var(--surface-muted); }
+.exercise-page { min-height: 100%; padding-bottom: 86px; }
+.exercise-toolbar { position: sticky; z-index: 4; top: 0; display: grid; min-height: 46px; grid-template-columns: 1fr auto 1fr; align-items: center; padding: 0 16px; background: white; border-bottom: 1px solid var(--line); }
+.exercise-toolbar > span { color: var(--ink-soft); font-size: 11px; }
+.exercise-toolbar strong { font-size: 14px; }
+.exercise-toolbar button { display: inline-flex; align-items: center; justify-self: end; padding: 7px 9px; color: var(--brand-strong); background: var(--brand-pale); border: 0; border-radius: 4px; font-size: 12px; gap: 5px; }
+.article-section, .questions-section { max-width: 860px; margin: 0 auto; background: white; border-inline: 1px solid var(--line); }
+.article-section { padding: 24px 26px 28px; border-bottom: 1px solid var(--line); }
+.article-section h2 { margin: 14px 0 18px; font-size: 23px; line-height: 1.3; letter-spacing: 0; }
+.meta-row { display: flex; flex-wrap: wrap; gap: 7px; }
+.meta-row span { padding: 4px 7px; color: var(--brand-strong); background: var(--brand-pale); border-radius: 4px; font-size: 11px; }
+.article-content { color: #24302e; font-family: Georgia, 'Times New Roman', serif; font-size: 17px; line-height: 1.9; }
+.article-content ::ng-deep p { margin: 0 0 14px; }
+.article-content ::ng-deep b { color: #1857b8; font-size: 1.2rem; font-weight: 750; }
+.generation-state { display: flex; max-width: 860px; min-height: 150px; align-items: center; justify-content: center; margin: 10px auto 0; padding: 24px; color: var(--brand-strong); background: white; border-block: 1px solid var(--line); gap: 13px; }
+.generation-state strong { font-size: 14px; }
+.generation-state p { margin: 5px 0 0; color: var(--ink-soft); font-size: 11px; }
+.questions-section { margin-top: 10px; }
+.question-block { padding: 22px 26px; border-bottom: 1px solid var(--line); }
+.question-block > header { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; gap: 10px; }
+.question-block > header > span { padding-top: 2px; color: var(--brand-strong); font-size: 12px; font-weight: 750; }
+.question-block > header strong { font-size: 15px; line-height: 1.5; }
+.question-block > header lucide-icon:not(.correct) { color: var(--danger); }
+.question-block > header lucide-icon.correct { color: var(--brand-strong); }
+.options-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 16px; gap: 9px; }
+.option { display: grid; min-height: 54px; grid-template-columns: 30px minmax(0, 1fr) auto; align-items: center; padding: 8px 10px; color: var(--ink); text-align: left; background: white; border: 1px solid var(--line); border-radius: var(--radius-sm); gap: 9px; }
+.option > span { display: grid; width: 28px; height: 28px; color: var(--ink-soft); background: var(--surface-muted); border-radius: 50%; font-size: 12px; font-weight: 700; place-items: center; }
+.option b { min-width: 0; overflow-wrap: anywhere; font-size: 13px; font-weight: 550; }
+.option.selected { border-color: var(--brand-strong); box-shadow: 0 0 0 2px rgb(39 125 105 / 10%); }
+.option.selected > span, .option.correct-option > span { color: white; background: var(--brand-strong); }
+.option.correct-option { color: #165e4d; background: #eff9f6; border-color: #65aa98; }
+.option.wrong-option { color: #973737; background: #fff3f3; border-color: #d98686; }
+.answer-analysis { margin-top: 14px; padding: 13px 14px; background: var(--surface-muted); border-left: 3px solid var(--brand); }
+.answer-analysis > strong { font-size: 13px; }
+.answer-analysis p { margin: 5px 0; color: var(--ink-soft); font-size: 12px; line-height: 1.55; }
+.answer-analysis small { display: block; margin-top: 6px; color: var(--ink-soft); line-height: 1.5; }
+.answer-footer { position: fixed; z-index: 5; display: flex; right: 0; bottom: 0; left: 0; min-height: 68px; align-items: center; justify-content: center; padding: 10px 16px; background: white; border-top: 1px solid var(--line); box-shadow: 0 -7px 20px rgb(24 54 47 / 8%); gap: 8px; }
+.submit-button { display: flex; width: min(550px, calc(100% - 150px)); align-items: center; justify-content: center; gap: 7px; }
+.submit-button.retry { background: #a97720; }
+.footer-tool { display: flex; min-width: 64px; min-height: 44px; align-items: center; justify-content: center; color: var(--ink-soft); background: white; border: 1px solid var(--line); border-radius: var(--radius-sm); flex-direction: column; font-size: 10px; gap: 2px; }
+.footer-tool.new-button { color: var(--brand-strong); border-color: var(--brand); }
+.page-state { display: flex; min-height: 360px; align-items: center; justify-content: center; padding: 24px; color: var(--ink-soft); text-align: center; flex-direction: column; gap: 8px; }
+.page-state p { margin: 0 0 10px; }
+.error-state lucide-icon { color: var(--danger); }
+.skeleton, .text-skeleton i { display: block; background: #edf1f0; animation: pulse 1.3s ease-in-out infinite; }
+.title-skeleton { width: 58%; height: 28px; margin: 20px 0; border-radius: 4px; }
+.text-skeleton { display: grid; gap: 11px; }
+.text-skeleton i { height: 14px; border-radius: 3px; }
+.text-skeleton i:nth-child(2) { width: 94%; }.text-skeleton i:nth-child(3) { width: 86%; }.text-skeleton i:nth-child(4) { width: 64%; }
+.dialog-backdrop { position: fixed; z-index: 30; display: grid; inset: 0; padding: 16px; background: rgb(14 25 22 / 56%); place-items: center; }
+.history-dialog { width: min(640px, 100%); max-height: 84dvh; overflow: hidden; background: white; border-radius: var(--radius-md); box-shadow: var(--shadow-lg); }
+.history-dialog > header { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px; border-bottom: 1px solid var(--line); }
+.history-dialog h2 { margin: 0; font-size: 18px; }.history-dialog header p { margin: 3px 0 0; color: var(--ink-soft); font-size: 11px; }.history-dialog header button { display: grid; width: 38px; height: 38px; color: var(--ink-soft); background: transparent; border: 0; place-items: center; }
+.history-list { max-height: calc(84dvh - 72px); overflow: auto; }.history-item { display: grid; width: 100%; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; padding: 15px 18px; color: var(--ink); text-align: left; background: white; border: 0; border-bottom: 1px solid var(--line); gap: 10px; }.history-item strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.history-item p { margin: 5px 0 0; color: var(--ink-soft); font-size: 11px; }.history-item > span { color: var(--brand-strong); font-size: 12px; }.dialog-state { display: flex; min-height: 240px; align-items: center; justify-content: center; color: var(--ink-soft); flex-direction: column; gap: 9px; }
+.load-more { display: block; min-height: 42px; margin: 12px auto; padding: 8px 18px; color: var(--brand-strong); background: white; border: 1px solid var(--brand); border-radius: var(--radius-sm); }.no-more { margin: 14px; color: var(--ink-soft); text-align: center; font-size: 11px; }
+.result-backdrop { position: fixed; z-index: 40; display: grid; inset: 0; padding: 18px; background: rgb(14 25 22 / 60%); place-items: center; }.result-dialog { width: min(420px, 100%); padding: 25px 22px 22px; text-align: center; background: white; border-top: 4px solid var(--danger); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); }.result-dialog.success { border-top-color: var(--brand-strong); }.result-dialog.warning { border-top-color: var(--accent); }.result-dialog h2 { margin: 10px 0 7px; font-size: 21px; letter-spacing: 0; }.result-dialog p { min-height: 42px; margin: 0 0 18px; color: var(--ink-soft); line-height: 1.5; white-space: pre-line; }.result-dialog button { width: 100%; }.result-icon { color: var(--danger); }.result-dialog.success .result-icon { color: var(--brand-strong); }.result-dialog.warning .result-icon { color: var(--accent); }
+.spin { animation: spin .8s linear infinite; }@keyframes spin { to { transform: rotate(360deg); } }@keyframes pulse { 50% { opacity: .45; } }
+@media (max-width: 620px) { .article-section, .question-block { padding-inline: 16px; }.article-section h2 { font-size: 20px; }.article-content { font-size: 16px; }.options-grid { grid-template-columns: 1fr; }.submit-button { width: min(100%, calc(100% - 72px)); }.answer-footer { padding-inline: 10px; }.new-button { position: absolute; right: 10px; bottom: 74px; background: white; box-shadow: 0 5px 15px rgb(24 54 47 / 14%); } }

Некоторые файлы не были показаны из-за большого количества измененных файлов